├── LICENSE ├── README.md ├── __init__.mojo ├── list.mojo ├── maybe.mojo ├── rc.mojo └── test.mojo /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Team Puzel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### A basic resizable and reference counted List implementation for Mojo 2 | Supporting `for .. in` iteration and many convenience methods such as `map`, `append`, `insert`, `remove`, `reverse`, `reversed`, `filter`, `fold`, `zip` etc. 3 | 4 | Also includes an implementation of `Optional` and `RcPointer` which were required by the list. 5 | 6 | You can see examples of how it can be used by looking in "test.mojo". 7 | 8 | examples: 9 | ```nim 10 | 11 | from list import List 12 | 13 | fn test_append(): 14 | var list: List[Int] = [1, 2, 3, 4] 15 | 16 | list.append(5) 17 | list.append([2, 2, 2]) 18 | 19 | for item in list: print(item) 20 | # outputs 1 2 3 4 5 2 2 2 21 | 22 | fn test_map(): 23 | fn square(num: Int) -> Int: return num * num 24 | 25 | let list: List[Int] = [2, 2, 2] 26 | let squared = list.map[Int](square) 27 | 28 | for item in list: print(item) 29 | for item in squared: print(item) 30 | # outputs 2 2 2 and 4 4 4 31 | 32 | fn test_fold(): 33 | fn sum(acc: Int, val: Int) -> Int: return acc + val 34 | 35 | let list: List[Int] = [1, 2, 3] 36 | let folded = list.fold[Int](0, sum) 37 | 38 | for item in list: print(item) 39 | print(folded) 40 | # outputs 1 2 3 and 6 41 | 42 | fn test_filter(): 43 | fn greater(num: Int) -> Bool: return num > 2 44 | 45 | let list: List[Int] = [1, 2, 3, 4] 46 | let filtered = list.filter(greater) 47 | 48 | for item in list: print(item) 49 | for item in filtered: print(item) 50 | # outputs 1 2 3 4 and 3 4 51 | ``` 52 | -------------------------------------------------------------------------------- /__init__.mojo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamPuzel/mojo-list/f64f01d0c2b5306028f6c9a24eea0c1814a2d0a8/__init__.mojo -------------------------------------------------------------------------------- /list.mojo: -------------------------------------------------------------------------------- 1 | 2 | from rc import RcPointer 3 | from maybe import Maybe 4 | 5 | # TASKS: 6 | # [x] - Figure out why insert and friends are getting ub 7 | # [ ] - Write tests for all features 8 | # [ ] - Optimize many of the methods 9 | # [ ] - Implement a lazy list to help avoid copying 10 | # [ ] - Implement a Slice type 11 | # [ ] - Implement copy on write 12 | 13 | struct List[T: AnyType]: 14 | var storage: RcPointer[T] 15 | var count: Int 16 | var capacity: Int 17 | 18 | fn __init__(inout self): 19 | self.count = 0 20 | self.capacity = 10 21 | self.storage = RcPointer[T](10) 22 | 23 | # where T: Copy 24 | fn __init__(inout self, repeating: T, count: Int): 25 | self.count = count 26 | self.capacity = count 27 | self.storage = RcPointer[T](count) 28 | for i in range(count): self.storage.store(i, repeating) 29 | 30 | fn __init__[*Ts: AnyType](inout self, owned literal: ListLiteral[Ts]): 31 | let req_len = len(literal) 32 | self.count = req_len 33 | self.capacity = req_len 34 | self.storage = RcPointer[T](req_len) 35 | let src = Pointer.address_of(literal).bitcast[T]() 36 | for i in range(req_len): 37 | self.storage.store(i, src.load(i)) 38 | 39 | fn __getitem__(self, i: Int) -> T: 40 | return self.storage.load(i) 41 | 42 | fn __setitem__(inout self, i: Int, value: T): 43 | self.storage.store(i, value) 44 | 45 | fn __len__(self) -> Int: 46 | return self.count 47 | 48 | fn __iter__(self) -> ListIterator[T]: 49 | return ListIterator[T](self.storage, self.count) 50 | 51 | fn __moveinit__(inout self, owned previous: Self): 52 | self.count = previous.count 53 | self.capacity = previous.capacity 54 | self.storage = previous.storage 55 | 56 | fn copy(self) -> Self: 57 | var new = Self() 58 | new.reserve_capacity(self.count) 59 | for item in self: new.append(item) 60 | return new^ 61 | 62 | @always_inline 63 | fn is_empty(self) -> Bool: return self.count == 0 64 | 65 | # Note this is not especially efficient without the lazy list 66 | # fn enumerated(self) -> List[(Int, T)]: 67 | # var buf = List[(Int, T)]() 68 | # buf.reserve_capacity(self.count) 69 | # for i in range(self.count): 70 | # buf.append((i, self[i])) 71 | # return buf^ 72 | 73 | # REQUIRES(Traits) where T: Eq 74 | # fn __eq__(self, rhs: Self) -> Bool: 75 | # if self.count != rhs.count: return False 76 | # for i in range(self.count): 77 | # if self[i] == rhs[i]: return False 78 | # return True 79 | 80 | # REQUIRES(Traits) where T: Ord 81 | # fn sort(): 82 | # fn sorted() -> Self: 83 | 84 | fn subrange(self, `from`: Int, count: Int) -> Self: 85 | var buf = Self() 86 | buf.reserve_capacity(self.count) 87 | for i in range(`from`, `from` + count): 88 | buf.append(self[i]) 89 | return buf^ 90 | 91 | @always_inline 92 | fn prefix(self, count: Int) -> Self: 93 | return self.subrange(0, count) 94 | 95 | @always_inline 96 | fn suffix(self, count: Int) -> Self: 97 | return self.subrange(self.last_index() - count, self.last_index()) 98 | 99 | fn first(self, where: fn(T) -> Bool) -> Maybe[T]: 100 | for item in self: 101 | if where(item): return Maybe[T](item) 102 | return Maybe[T]() 103 | 104 | fn first(self, where: fn(T) capturing -> Bool) -> Maybe[T]: 105 | for item in self: 106 | if where(item): return Maybe[T](item) 107 | return Maybe[T]() 108 | 109 | fn all_satisfy(self, predicate: fn(T) -> Bool) -> Bool: 110 | for item in self: 111 | if not predicate(item): return False 112 | return True 113 | 114 | fn all_satisfy(self, predicate: fn(T) capturing -> Bool) -> Bool: 115 | for item in self: 116 | if not predicate(item): return False 117 | return True 118 | 119 | # REQUIRES(Traits) where T: Eq 120 | # fn first_index(self, of: T) -> Int: 121 | 122 | # REQUIRES(Traits) where T: Eq 123 | # fn contains(self, value: T) -> Bool: 124 | 125 | fn resize(inout self, by: Int): 126 | let new_capacity = self.capacity + by 127 | let new = RcPointer[T](new_capacity) 128 | for i in range(self.count): 129 | new.store(i, self.storage.load(i)) 130 | self.storage = new 131 | self.capacity = new_capacity 132 | 133 | fn append(inout self, value: T): 134 | if self.count >= self.capacity: 135 | self.resize(self.capacity * 2) 136 | self[self.count] = value 137 | self.count += 1 138 | 139 | fn append(inout self, list: List[T]): 140 | for item in list: self.append(item) 141 | 142 | @always_inline 143 | fn drop_last(inout self): self.count -= 1 144 | 145 | fn remove_last(inout self) -> T: 146 | self.count -= 1 147 | return self[self.count] 148 | 149 | fn remove(inout self, at: Int) -> T: 150 | let ret = self[at] 151 | let last = self.last_index() 152 | self.count -= 1 153 | for i in range(at, last): 154 | self[i] = self[i + 1] 155 | return ret 156 | 157 | # A faster version of remove, does not need to shift elements. 158 | # It does so at the cost of changing the order of elements. 159 | fn swap_remove(inout self, at: Int) -> T: 160 | self[at] = self[self.last_index()] 161 | return self.remove_last() 162 | 163 | fn insert(inout self, value: T, at: Int): 164 | self.unsafe_shift_right(at) 165 | self[at] = value 166 | 167 | fn unsafe_shift_right(inout self, `from`: Int): 168 | let new_count = self.count + 1 169 | if self.capacity <= new_count: self.resize(new_count) 170 | self.count = new_count 171 | for i in range(self.last_index(), `from` - 1, -1): 172 | if i == self.last_index(): continue 173 | self[i + 1] = self[i] 174 | 175 | fn first(self) -> T: return self[0] 176 | fn last(self) -> T: return self[self.last_index()] 177 | 178 | @always_inline 179 | fn last_index(self) -> Int: return self.count - 1 180 | 181 | fn reversed(self) -> Self: 182 | var buf = Self() 183 | buf.reserve_capacity(self.count) 184 | buf.count = self.count 185 | 186 | var offset: Int = self.last_index() 187 | for item in self: 188 | buf[offset] = item 189 | offset -= 1 190 | return buf^ 191 | 192 | fn reverse(inout self): self = self.reversed() 193 | 194 | fn reserve_capacity(inout self, capacity: Int): 195 | if self.capacity < capacity: 196 | self.resize(capacity) 197 | 198 | fn map[A: AnyType](self, body: fn(T) capturing -> A) -> List[A]: 199 | var buf = List[A]() 200 | buf.reserve_capacity(self.count) 201 | for item in self: buf.append(body(item)) 202 | return buf^ 203 | 204 | fn map[A: AnyType](self, body: fn(T) -> A) -> List[A]: 205 | var buf = List[A]() 206 | buf.reserve_capacity(self.count) 207 | for item in self: buf.append(body(item)) 208 | return buf^ 209 | 210 | fn for_each(self, body: fn(T) capturing -> None): 211 | for item in self: body(item) 212 | 213 | fn for_each(self, body: fn(T) -> None): 214 | for item in self: body(item) 215 | 216 | fn filter(self, body: fn(T) capturing -> Bool) -> List[T]: 217 | var buf = List[T]() 218 | for item in self: 219 | if body(item): buf.append(item) 220 | return buf^ 221 | 222 | fn filter(self, body: fn(T) -> Bool) -> List[T]: 223 | var buf = List[T]() 224 | for item in self: 225 | if body(item): buf.append(item) 226 | return buf^ 227 | 228 | fn fold[A: AnyType](self, owned into: A, body: fn(A, T) capturing -> A) -> A: 229 | var acc = into 230 | for item in self: 231 | acc = body(acc, item) 232 | return acc 233 | 234 | fn fold[A: AnyType](self, owned into: A, body: fn(A, T) -> A) -> A: 235 | var acc = into 236 | for item in self: 237 | acc = body(acc, item) 238 | return acc 239 | 240 | fn zip[A: AnyType, B: AnyType](self, `with`: List[A], body: fn(A, T) capturing -> B) -> List[B]: 241 | var least = 0 242 | if self.count < `with`.count: least = self.count else: least = `with`.count 243 | var buf = List[B]() 244 | buf.reserve_capacity(least) 245 | for i in range(least): 246 | buf.append(body(`with`[i], self[i])) 247 | return buf^ 248 | 249 | fn zip[A: AnyType, B: AnyType](self, `with`: List[A], body: fn(A, T) -> B) -> List[B]: 250 | var least = 0 251 | if self.count < `with`.count: least = self.count else: least = `with`.count 252 | var buf = List[B]() 253 | buf.reserve_capacity(least) 254 | for i in range(least): 255 | buf.append(body(`with`[i], self[i])) 256 | return buf^ 257 | 258 | struct ListIterator[T: AnyType]: 259 | var offset: Int 260 | var max: Int 261 | var storage: RcPointer[T] 262 | 263 | fn __init__(inout self, storage: RcPointer[T], max: Int): 264 | self.offset = 0 265 | self.max = max 266 | self.storage = storage 267 | 268 | fn __len__(self) -> Int: 269 | return self.max - self.offset 270 | 271 | fn __next__(inout self) -> T: 272 | let ret = self.storage.load(self.offset) 273 | self.offset += 1 274 | return ret 275 | 276 | # REQUIRES(Traits) 277 | # struct LazyList[T: AnyType]: 278 | # var list: List[T] -------------------------------------------------------------------------------- /maybe.mojo: -------------------------------------------------------------------------------- 1 | 2 | struct Maybe[T: AnyType]: 3 | var raw_value: T 4 | var wrapping: Bool 5 | 6 | fn __init__(inout self): 7 | self.raw_value = rebind[T, Int](0) 8 | self.wrapping = False 9 | 10 | fn __init__(inout self, owned value: T): 11 | self.raw_value = value 12 | self.wrapping = True 13 | 14 | fn unwrap(self) -> T: 15 | debug_assert(self.is_some(), "Unwrapped a none value") 16 | return self.raw_value 17 | 18 | fn is_some(self) -> Bool: return self.wrapping 19 | fn is_none(self) -> Bool: return not self.wrapping 20 | 21 | fn if_some(self, then: fn(T) -> None): 22 | if self.is_some(): then(self.unwrap()) 23 | 24 | fn if_some(self, then: fn(T) capturing -> None): 25 | if self.is_some(): then(self.unwrap()) 26 | 27 | fn if_none(self, then: fn() -> None): 28 | if self.is_none(): then() 29 | 30 | fn if_none(self, then: fn() capturing -> None): 31 | if self.is_none(): then() -------------------------------------------------------------------------------- /rc.mojo: -------------------------------------------------------------------------------- 1 | 2 | from memory.unsafe import Pointer 3 | from maybe import Maybe 4 | 5 | # not sure why I had to use 2 pointers 6 | # but Mojo did not let me have nested generics 7 | # and still store into the pointer. 8 | struct RcPointer[T: AnyType]: 9 | # Modifying this externally is undefined 10 | # which is currently not possible to enforce 11 | var references: Pointer[Int] 12 | var storage: Pointer[T] 13 | 14 | fn __init__(inout self, value: Pointer[T], count: Int): 15 | self.references = Pointer[Int].alloc(1) 16 | self.references.store(0, 1) 17 | self.storage = Pointer[T].alloc(count) 18 | for i in range(count): 19 | self.storage.store(i, value.load(i)) 20 | 21 | fn __init__(inout self, capacity: Int): 22 | self.references = Pointer[Int].alloc(1) 23 | self.references.store(0, 1) 24 | self.storage = Pointer[T].alloc(capacity) 25 | 26 | fn __init__(inout self, value: T): 27 | self.references = Pointer[Int].alloc(1) 28 | self.references.store(0, 1) 29 | self.storage = Pointer[T].alloc(1) 30 | self.storage.store(0, value) 31 | 32 | fn __copyinit__(inout self, previous: Self): 33 | self.references = previous.references 34 | self.storage = previous.storage 35 | self.references.store(0, self.references.load(0) + 1) 36 | 37 | fn __del__(owned self): 38 | self.references.store(0, self.references.load(0) - 1) 39 | if self.references.load(0) <= 0: 40 | self.references.free() 41 | self.storage.free() 42 | self.storage = Pointer[T].get_null() 43 | 44 | fn weak_ref(self) -> WeakRcPointer[T]: 45 | return WeakRcPointer[T](self.references, self.storage) 46 | 47 | fn load(self, index: Int) -> T: 48 | return self.storage.load(index) 49 | 50 | fn store(self, index: Int, value: T): 51 | self.storage.store(index, value) 52 | 53 | struct WeakRcPointer[T: AnyType]: 54 | var references: Pointer[Int] 55 | var storage: Pointer[T] 56 | 57 | fn __init__(inout self, references: Pointer[Int], ptr: Pointer[T]): 58 | self.references = references 59 | self.storage = ptr 60 | 61 | fn __copyinit__(inout self, previous: Self): 62 | self.references = previous.references 63 | self.storage = previous.storage 64 | 65 | fn is_valid(self) -> Bool: return self.storage == Pointer[T].get_null() 66 | 67 | fn try_load(self, index: Int) -> Maybe[T]: 68 | if self.is_valid(): return Maybe[T](self.storage.load(index)) 69 | else: return Maybe[T]() 70 | 71 | fn try_store(self, index: Int, value: T) -> Bool: 72 | if self.is_valid(): 73 | self.storage.store(index, value) 74 | return True 75 | else: return False -------------------------------------------------------------------------------- /test.mojo: -------------------------------------------------------------------------------- 1 | 2 | from list import List 3 | from maybe import Maybe 4 | # from testing import * 5 | 6 | fn spacer(): 7 | print("") 8 | fn red(): 9 | # ??? 10 | print_no_newline(chr(27)) 11 | print_no_newline("[31m") 12 | fn blue(): 13 | print_no_newline(chr(27)) 14 | print_no_newline("[34m") 15 | fn green(): 16 | print_no_newline(chr(27)) 17 | print_no_newline("[32m") 18 | fn reset(): 19 | print_no_newline(chr(27)) 20 | print_no_newline("[0m") 21 | fn test_name(str: String): 22 | blue() 23 | print("\n--- " + str + " ---") 24 | reset() 25 | 26 | # Temporary test function until traits are added 27 | fn int_list_eq(lhs: List[Int], rhs: List[Int]) -> Bool: 28 | if lhs.count != rhs.count: return False 29 | for i in range(lhs.count): 30 | if lhs[i] != rhs[i]: return False 31 | return True 32 | 33 | fn assert_list_eq(lhs: List[Int], rhs: List[Int]): 34 | spacer() 35 | if int_list_eq(lhs, rhs): 36 | green() 37 | print_no_newline("TEST SUCCESFUL") 38 | else: 39 | red() 40 | print_no_newline("TEST FAILED") 41 | reset() 42 | 43 | fn assert_true(condition: Bool): 44 | spacer() 45 | if condition: 46 | green() 47 | print_no_newline("TEST SUCCESFUL") 48 | else: 49 | red() 50 | print_no_newline("TEST FAILED") 51 | reset() 52 | 53 | fn main(): 54 | test_name("MAYBE") 55 | test_maybe() 56 | spacer() 57 | 58 | test_name("COPY") 59 | test_copy() 60 | spacer() 61 | 62 | test_name("APPEND") 63 | test_append() 64 | spacer() 65 | 66 | test_name("MAP") 67 | test_map() 68 | spacer() 69 | 70 | test_name("FILTER") 71 | test_filter() 72 | spacer() 73 | 74 | test_name("FOLD") 75 | test_fold() 76 | spacer() 77 | 78 | test_name("REVERSE") 79 | test_reversed() 80 | spacer() 81 | 82 | test_name("ZIP") 83 | test_zip() 84 | spacer() 85 | 86 | test_name("PREFIX") 87 | test_prefix() 88 | spacer() 89 | 90 | test_name("INSERT") 91 | test_insert() 92 | spacer() 93 | 94 | test_name("REMOVE") 95 | test_remove() 96 | spacer() 97 | 98 | test_name("REMOVE LAST") 99 | test_remove_last() 100 | spacer() 101 | 102 | test_name("SWAP REMOVE") 103 | test_swap_remove() 104 | spacer() 105 | 106 | fn test_append(): 107 | var list: List[Int] = [1, 2, 3, 4] 108 | 109 | list.append(5) 110 | list.append([2, 2, 2]) 111 | 112 | for item in list: print_no_newline(item, " ") 113 | assert_list_eq(list, [1, 2, 3, 4, 5, 2, 2, 2]) 114 | 115 | fn test_copy(): 116 | let list: List[Int] = [2, 2, 2] 117 | let a_copy = list.copy() 118 | 119 | for item in a_copy: print_no_newline(item, " ") 120 | assert_list_eq(list, a_copy) 121 | 122 | fn test_map(): 123 | fn square(num: Int) -> Int: return num * num 124 | 125 | let list: List[Int] = [2, 2, 2] 126 | let squared = list.map[Int](square) 127 | 128 | print("List:") 129 | for item in list: print_no_newline(item, " ") 130 | print("\nSquared:") 131 | for item in squared: print_no_newline(item, " ") 132 | assert_list_eq(squared, [4, 4, 4]) 133 | 134 | fn test_filter(): 135 | fn greater(num: Int) -> Bool: return num > 2 136 | 137 | let list: List[Int] = [1, 2, 3, 4] 138 | let filtered = list.filter(greater) 139 | 140 | print("List:") 141 | for item in list: print_no_newline(item, " ") 142 | print("\nFiltered:") 143 | for item in filtered: print_no_newline(item, " ") 144 | assert_list_eq(filtered, [3, 4]) 145 | 146 | fn test_fold(): 147 | fn sum(acc: Int, val: Int) -> Int: return acc + val 148 | 149 | let list: List[Int] = [1, 2, 3] 150 | let folded = list.fold[Int](0, sum) 151 | 152 | print("List:") 153 | for item in list: print_no_newline(item, " ") 154 | print("\nFolded:") 155 | print_no_newline(folded) 156 | assert_true(folded == 6) 157 | 158 | fn test_reversed(): 159 | let list: List[Int] = [1, 2, 3] 160 | let reversed = list.reversed() 161 | 162 | print("List:") 163 | for item in list: print_no_newline(item, " ") 164 | print("\nReversed:") 165 | for item in reversed: print_no_newline(item, " ") 166 | assert_list_eq(reversed, [3, 2, 1]) 167 | 168 | fn test_zip(): 169 | fn multiply(left: Int, right: Int) -> Int: return left * right 170 | 171 | let list1: List[Int] = [1, 2, 3] 172 | let list2: List[Int] = [1, 4, 8] 173 | 174 | let list3 = list1.zip[Int, Int](list2, multiply) 175 | 176 | for item in list3: print_no_newline(item, " ") 177 | assert_list_eq(list3, [1, 8, 24]) 178 | 179 | fn test_prefix(): 180 | let list: List[Int] = [1, 2, 3, 4, 5, 6, 7, 8] 181 | 182 | let first_three = list.prefix(3) 183 | 184 | for item in first_three: print_no_newline(item, " ") 185 | assert_list_eq(first_three, [1, 2, 3]) 186 | 187 | fn test_insert(): 188 | var list: List[Int] = [1, 3, 4, 5] 189 | list.insert(2, 1) 190 | 191 | for item in list: print_no_newline(item, " ") 192 | assert_list_eq(list, [1, 2, 3, 4, 5]) 193 | 194 | fn test_maybe(): 195 | fn try_print(value: Int): print(value) 196 | fn handle_none(): print_no_newline("No value!") 197 | 198 | let wrapped = Maybe[Int](1) 199 | let none = Maybe[Int]() 200 | 201 | wrapped.if_some(try_print) 202 | none.if_some(try_print) 203 | 204 | wrapped.if_none(handle_none) 205 | none.if_none(handle_none) 206 | 207 | fn test_remove(): 208 | var list: List[Int] = [1, 4, 2, 3, 4, 5, 6] 209 | 210 | print("List:") 211 | for item in list: print_no_newline(item, " ") 212 | 213 | _ = list.remove(1) 214 | 215 | print("\nNew:") 216 | for item in list: print_no_newline(item, " ") 217 | assert_list_eq(list, [1, 2, 3, 4, 5, 6]) 218 | 219 | fn test_remove_last(): 220 | var list: List[Int] = [1, 2, 3] 221 | 222 | print("List:") 223 | for item in list: print_no_newline(item, " ") 224 | 225 | let last = list.remove_last() 226 | 227 | print("\nLast:") 228 | print_no_newline(last) 229 | assert_true(last == 3) 230 | print("\nModified List:") 231 | for item in list: print_no_newline(item, " ") 232 | assert_list_eq(list, [1, 2]) 233 | 234 | fn test_swap_remove(): 235 | var list: List[Int] = [1, 2, 3, 4] 236 | 237 | print("List:") 238 | for item in list: print_no_newline(item, " ") 239 | 240 | _ = list.swap_remove(1) 241 | 242 | print("\nNew:") 243 | for item in list: print_no_newline(item, " ") 244 | assert_list_eq(list, [1, 4, 3]) 245 | 246 | #fn test_enumerate(): 247 | # var list: List[Int] = [1, 2, 3, 4] 248 | # 249 | # for item in list.enumerated(): 250 | # print(item[0]) 251 | # print(item[1]) --------------------------------------------------------------------------------