I'm just going through some Swift tuts that are obviously already outdated as of Beta3 ...
func exchange<T>(data:[T], i:Int, j:Int)
{
let temp = data[i];
data[i] = data[j];
data[j] = temp;
}
Playgrounds tells me:
Error: #lvalue $T8 is not identical to T.
How do I change it to make it work?
Arrays in Swift are value types. That means that data is copied when passed into your exchange method, but you are trying to modify the copy to affect the original version. Instead you should do one of two things:
1. Define data as an inout parameter:
func exchange<T>(inout data:[T], i:Int, j:Int)
Then when calling it you have to add an & before the call:
var myArray = ["first", "second"]
exchange(&myArray, 0, 1)
2. Return a copy of the Array (recommended)
func exchange<T>(data:[T], i:Int, j:Int) -> [T]
{
var newData = data
newData[i] = data[j]
newData[j] = data[i]
return newData
}
I recommend this way over the in-out parameter because in-out parameters create more complicated state. You have two variables pointing to and potentially manipulating the same piece of memory. What if exchange decided to do its work on a separate thread? There is also a reason that Apple decided to make arrays value types, using in-out subverts that. Finally, returning a copy is much closer to Functional Programming which is a promising direction that Swift can move. The less state we have in our apps, the fewer bugs we will create (in general).
Related
let mut args: Vec<String> = args().collect();
for mut i in 0..args.len() {
if args[i].contains("-type") {
project_type = args[i].split("=").last().unwrap();
args.remove(i);
i-=1;
}
}
I'm very new to rust and want to know why this code gives me the error "cannot borrow 'args' as mutable because it is also borrowed as immutable" and how i can fix it
I'm trying to check if the args Vec contains an item like "-type=some_type", then put "some_type" in a variable called "project_type". Finally I want to remove the item "-type=some_type" from the list and keep searching.
edit: if I'm doing what I'm trying to do wrong, i would appreciate a better solution.
str::split doesn't create new string objects, it returns references to the existing one. So your project_type variable is a reference to a string in args. When you remove from args, you invalidate all of those references.
You can convert a &str to a String and break the reference with .to_owned()
project_type = args[i].split("=").last().unwrap().to_owned();
Now, for loops in Rust iterate over data structures. As such, your i -= 1 trick won't work. i will just get reset back to where it was at the next iteration. If you want to do this the way you're currently doing it, you want a while loop.
let mut i = 0
while i < args.len() {
if ... {
...
} else {
i += 1;
}
}
However, let's think about this a bit more functionally. What it sounds like you want is to find a matching element of a vector (and its index) and then do something with it. So let's break that down into two separate problems.
We use enumerate to get a collection together with its index, and find to search it. Since it looks like you expect the value to be there, we'll unwrap it and panic if it's not there. If you want to handle the error, convert this to a match.
let (idx, element) = args.iter().find(|x| x.contains("-type")).unwrap();
Once we have it, we can do our work on element to get the project type.
project_type = element.split("=").last().unwrap().to_owned();
Note that at this point project_type does not depend on args at all. element does, and Rust may be smart enough to see this, but let's just be perfectly safe and make sure element gets dropped before we modify args. Once there are no more references to args, we can mutably borrow to do the remove operation.
let idx = {
let (idx, element) = args.iter().enumerate().find(|(_, x)| x.contains("-type")).unwrap();
project_type = element.split("=").last().unwrap().to_owned();
idx
}
args.remove(idx);
I have an array of value pairs I want to modify. I need to add and remove values from this array as well, so I used a list. When I tried to use a list, I encountered an error.
Error CS1612 - Cannot modify the return value of 'List<(int, float)>.this[int]' because it is not a variable
So I decided I would investigate. I tried using an array instead, and it... worked fine? The following code only throws an error on arr1[0].Item1 += 1;.
static void Main()
{
List<(int, float)> arr1 = new List<(int, float)>() { (0, 0) };
(int, float)[] arr2 = new (int, float)[1];
arr1[0].Item1 += 1; // This line
arr2[0].Item1 += 1;
}
Why are tuple arrays mutable, but lists are not? Is this because arrays are simple blocks of data you can modify easily, but lists have a lot of backend behind them that complicates things? Is there a simple way to get around this, or am I going to have to make my own custom class?
Why are tuple arrays mutable, but lists are not?
The list itself is mutable, but not in the way you're doing it. Note that this isn't anything specific to tuples - it's just the case for any mutable struct.
The list indexer getter returns a value (i.e. a copy of the tuple in your case) - so modifying that value wouldn't modify the copy in the list. The compiler is trying to avoid you making a change to a value that's about to be thrown away. Array access doesn't do that - arr2[0] refers to the variable within the array. (An array is effectively a collection of variables.)
If you want to mutate the list, you can have to fetch the tuple, mutate it, then put it back:
var tuple = arr1[0];
tuple.Item1++;
arr1[0] = tuple;
Note that this also explains why you can't use list access expressions as arguments for ref parameters, but you can do the equivalent for arrays:
public void Method(ref int x) => x++;
public void CallMethod()
{
var list = new List<int> { 0 };
var array = new int[] { 0 };
Method(ref list[0]); // Error
Method(ref array[0]); // Valid
}
How can I use UnsafeMutablePointer<T?> as UnsafeMutablePointer<UnsafeRawPointer?>!
e.g. Trying to allocate n blocks of memory for type T, to get values from a CFSet cfsetref:
var array = UnsafeMutableRawPointer<T?>.allocate(capacity: n)
CFSetGetValues(cfsetref, array) // error
Which gives the error
Cannot convert value of type 'UnsafeMutablePointer<T?>' to expected
argument type 'UnsafeMutablePointer<UnsafeRawPointer?>!'
I tried declaring array as UnsafeMutablePointer<UnsafeRawPointer?> then doing
for i in 0..<n {
var d = array[i]
d?.bindMemory(to: T.self, capacity: 1)
}
But I still get EXC_BAD_INSTRUCTION errors when attempting to access array[i] (after binding the memory again to T)
Many things depends on how you get your cfsetref and what actually is T.
But anyway, CFSetGetValues expects UnsafeMutablePointer<UnsafeRawPointer?>! as shown in the error message.
let n = CFSetGetCount(cfsetref)
let array = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: n)
array.initialize(to: nil, count: n)
CFSetGetValues(cfsetref, array)
To safely access the contents of array, you need to know how T is managed by Swift ARC. For example, assuming T is NSNumber, you need to tell Swift ARC to manage the result with writing something like this:
let managedArray = UnsafeMutableBufferPointer(start: array, count: n).map{Unmanaged<NSNumber>.fromOpaque($0!).takeRetainedValue()}
print(managedArray)
But as well as other CF-collection types, the better way to handle CFSet is bridging it to Swift Set:
if let swiftSet = cfsetref as? Set<NSNumber> {
let swiftArray = Array(swiftSet)
print(swiftArray)
}
How do you get your cfsetref and what actually is T? With such information, I would try to tell you what sort of code you need to write in your actual case.
import Foundation
func insertionSort<T where T: Comparable>(var items:[T])-> [T] {
for (index, _) in items.enumerate().dropFirst() {
var j = index
while ((j > 0) && (items[j] < items[j-1])) {
swap(&items[j], &items[j-1])
j = j-1
}
}
return items
}
// Test the function
insertionSort([]) // Generic type array is not taking empty array
When I am trying to call insertionSort with empty array, I get
Cannot invoke 'insertionSort' with an argument list of type '([_])'
I am not able to figure out how to fix this.
To call generic functions in Swift, Swift needs to be able to infer the generic parameters.
One way giving the type information to Swift, is using an intermediate variable.
(Like noted in Lu_'s comment.)
let arr: [Int] = []
let result = insertionSort(arr)
Another way is using as.
let result = insertionSort([] as [Int])
(Remember, var parameter does not modify the actual argument. It just makes a mutable copy, but does not write it back to the original argument. Swift 3 removed var parameters, as it's so confusing. You may need to assign the return value of the function to a variable.)
How would I make an exact duplicate of an array?
I am having hard time finding information about duplicating an array in Swift.
I tried using .copy()
var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Arrays have full value semantics in Swift, so there's no need for anything fancy.
var duplicateArray = originalArray is all you need.
If the contents of your array are a reference type, then yes, this will only copy the pointers to your objects. To perform a deep copy of the contents, you would instead use map and perform a copy of each instance. For Foundation classes that conform to the NSCopying protocol, you can use the copy() method:
let x = [NSMutableArray(), NSMutableArray(), NSMutableArray()]
let y = x
let z = x.map { $0.copy() }
x[0] === y[0] // true
x[0] === z[0] // false
Note that there are pitfalls here that Swift's value semantics are working to protect you from—for example, since NSArray represents an immutable array, its copy method just returns a reference to itself, so the test above would yield unexpected results.
There is a third option to Nate's answer:
let z = x.map { $0 } // different array with same objects
* EDITED * edit starts here
Above is essentially the same as below and actually using the equality operator below will perform better since the array won't be copied unless it is changed (this is by design).
let z = x
Read more here: https://developer.apple.com/swift/blog/?id=10
* EDITED * edit ends here
adding or removing to this array won't affect the original array. However, changing any of the objects' any properties that the array holds would be seen in the original array. Because the objects in the array are not copies (assuming the array hold objects, not primitive numbers).
Nate is correct. If you are working with primitive arrays all you need to do is assign duplicateArray to the originalArray.
For the sake of completeness, if you were working an NSArray object, you would do the following to do a full copy of an NSArray:
var originalArray = [1, 2, 3, 4] as NSArray
var duplicateArray = NSArray(array:originalArray, copyItems: true)
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
If you want to copy the items of an array of some class object.
Then you can follow the below code without using NSCopying protocol but you need to have an init method which should take all the parameters that are required for your object.
Here is the code for an example to test on playground.
class ABC {
var a = 0
func myCopy() -> ABC {
return ABC(value: self.a)
}
init(value: Int) {
self.a = value
}
}
var arrayA: [ABC] = [ABC(value: 1)]
var arrayB: [ABC] = arrayA.map { $0.myCopy() }
arrayB.first?.a = 2
print(arrayA.first?.a)//Prints 1
print(arrayB.first?.a)//Prints 2