Here I got good advice to use Equitable for subviews for performance but I am struggling to implement that.
I have a complex view made up of diverse subviews. The subviews look like this.
struct Sub1View < Content : View > : View, Equatable
{
let x : Int // Here x represents some local state
let content : Content
var body : some View
{
content
}
init ( x : Int, #ViewBuilder content : () -> Content )
{
self.x = x
self.content = content()
}
static func == ( lhs : Sub1View < Content >, rhs : Sub1View < Content > ) -> Bool
{
return lhs.x == rhs.x
}
}
struct Sub2View < Content : View > : View, Equatable
{
// Similar to above
}
I want to use these inside the main view, which looks something like this
struct MainView : View
{
var body : some View
{
fn ( x : 1 )
fn ( x : 2 )
fn ( x : 3 )
fn ( x : 4 )
// more of the same
}
}
That function fn can return any one of the subviews and that is where I am stuck. FWIW in reality there are multiple functions, this is a simplified version of the actual code.
I tried implementing the function as follows
func fn ( x : Int ) -> some View
{
if x > 5
{
return Sub1View ( x : x )
{
Button ...
}
}
else
{
return Sub2View ( x : x )
{
Label ...
}
}
}
but then I run into Function declares an opaque return type, but the return statements in its body do not have matching underlying types. I was hoping that View would be the common denominator here.
Then I tried
#ViewBuilder
func fn ( x : Int ) -> some View
{
if x > 5
{
// No return
Sub1View ( x : x )
{
Button ...
}
}
else
{
// No return
Sub2View ( x : x )
{
Label ...
}
}
}
and, already frustrated, I got the really meaningful The compiler is unable to type-check this expression in reasonable time....
It seems I have to add some common protocol to all of them Sub1View and Sub2View and so on, further layering an already complex thing. Also, each time I add one more such layer the compile time goes up exponentially.
Anyhow, I tried that using a protocol like this
protocol SomeSubView
{
associatedtype ContentType
}
but still run into trouble. I am hoping somebody can help me with this please? I would really like to get away without having to add that protocol.
Earlier, before I added the Equitable, the function was simply
func fn ( x : Int ) -> some View
{
if x > 5
{
return Button ...
}
else
{
return Label ...
}
}
but now that I want to return structs, that conform to View, I am struggling, and I also wonder why I can't simply replace those Button and Labels with my own structs?
I have struct Queue<T> that is based on a LinkedList<T>() I want to be able to iterate over the element in the queue and do something with them.
After doing some digging, I believe I have to inherit from Sequence and do something like this:
extension Sequence {
public func makeIterator() -> CountableRange<T>.Iterator {
return (0..<self).makeIterator()
}
}
and after I can have a function in my Queue class something like:
func iter(q: T) -> T? {
for i in q {
}
}
except the extension throws Use of undeclared type 'T' and the for loop a Type 'T' does not conform to protocol 'Sequence'
I am fairly new to Swift and I understand what I have to do I just don't know how to do it and find most explanations quite confusing. Could someone point me in the right direction?
import Foundation
public struct Queue<T> : Sequence{
fileprivate var list = LinkedList<T>()
public var queueCount : Int {
return list.getCount()
}
public var isEmpty: Bool {
return list.isEmpty
}
public mutating func enqueue(_ element: T) {
list.append(value: element)
}
public mutating func dequeue() -> T? {
guard !list.isEmpty, let element = list.first else { return nil }
list.remove(node: element)
return element.value
}
public func peek() -> T? {
return list.first?.value
}
func iter(q: T) -> T? {
for i in q {
}
}
}
extension Queue: CustomStringConvertible {
// 2
public var description: String {
// 3
return list.description
}
}
extension Sequence {
public func makeIterator() -> CountableRange<T>.Iterator {
return (0..<self).makeIterator()
}
}
I am following Learning Rust With Entirely Too Many Linked Lists to write my first program in Rust. I changed the program to:
use std::mem;
#[derive(Debug)]
pub enum List {
Nil,
More(Box<Node>),
}
#[derive(Debug)]
pub struct Node {
val: i32,
next: List
}
impl List {
pub fn new() -> Self {
List::Nil
}
pub fn insert(&mut self, v : i32) {
let old_head = mem::replace(&mut *self, List::Nil);
let new_head = List::More(Box::new(Node { val : v, next: old_head}));
*self = new_head
}
pub fn remove(&mut self) -> Option<i32> {
match mem::replace(&mut *self, List::Nil) {
List::Nil => {
None
},
List::More(ref mut boxed_node) => {
let result = Some(boxed_node.val);
*self = mem::replace(&mut boxed_node.next, List::Nil);
result
}
}
}
}
impl Drop for List {
fn drop(&mut self) {
let mut head = mem::replace(&mut *self, List::Nil);
while let List::More(ref mut node) = mem::replace(&mut head, List::Nil) {
head = mem::replace(&mut node.next, List::Nil);
}
}
}
#[cfg(test)]
mod test {
use super::List;
#[test]
fn basics() {
let mut list = List::new();
list.insert(7);
assert_eq!(Some(7), list.remove());
assert_eq!(None, list.remove());
list.insert(1);
list.insert(2);
list.insert(3);
assert_eq!(Some(3), list.remove());
assert_eq!(Some(2), list.remove());
assert_eq!(Some(1), list.remove());
assert_eq!(None, list.remove());
}
#[test]
fn drop_long_list() {
let mut list = List::new();
for i in 1..100000 {
list.insert(i);
}
}
}
Both of my tests are failing with a stack overflow in drop. Is it because of *self in RHS?
I don't know what is happening with let mut head = mem::replace(&mut *self, List::Nil);.
My understanding is:
It sets List::Nil in self.
Puts original value of self in head.
Is *self doing something more?
I also tried this version of drop:
impl Drop for List {
fn drop(&mut self) {
let mut head = mem::replace(self, List::Nil);
while let List::More(ref mut node) = mem::replace(&mut head, List::Nil) {
head = mem::replace(&mut node.next, List::Nil);
}
}
}
Another try:
impl Drop for List {
fn drop(&mut self) {
while true {
match self {
&mut List::Nil => break,
&mut List::More(ref mut node) => {
*self = mem::replace(&mut node.next, List::Nil)
}
}
}
}
}
error[E0506]: cannot assign to `*self` because it is borrowed
--> src\list.rs:48:21
|
47 | &mut List::More(ref mut node) => {
| ------------ borrow of `*self` occurs here
48 | *self = mem::replace(&mut node.next, List::Nil)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `*self` occurs here
Whenever you write recursive (or iterative) code, you need to have a stopping condition. Your code doesn't, so it loops forever.
Producing a MCVE of your problem is always a good start:
use std::mem;
#[derive(Debug)]
pub enum List {
Nil,
More(Box<List>),
}
impl Drop for List {
fn drop(&mut self) {
let mut head = mem::replace(self, List::Nil);
while let List::More(ref mut node) = mem::replace(&mut head, List::Nil) {
head = mem::replace(node, List::Nil);
}
}
}
#[test]
fn basics() {
List::Nil;
}
Then annotate the code to see where it's recurring:
fn drop(&mut self) {
eprintln!("1");
let mut head = mem::replace(self, List::Nil);
eprintln!("2");
while let List::More(ref mut node) = mem::replace(&mut head, List::Nil) {
eprintln!("3");
head = mem::replace(node, List::Nil);
eprintln!("4");
}
eprintln!("5");
}
This prints out
1
2
1
2
so delete everything after that:
fn drop(&mut self) {
eprintln!("1");
let mut head = mem::replace(self, List::Nil);
eprintln!("2");
}
Why does this cause infinite recursion? You've defined it so that in order to drop List, you have to create a new List, which in turn needs to be dropped, which creates a new List, which...
Add a stopping condition:
fn drop(&mut self) {
if let List::Nil = *self { return }
let mut head = mem::replace(self, List::Nil);
while let List::More(ref mut node) = mem::replace(&mut head, List::Nil) {
head = mem::replace(node, List::Nil);
}
}
No more infinite recursion.
Then expand back out to the original and try again. It works for this test case, but not for List::More(Box::new(List::Nil)) so we shrink it back:
fn drop(&mut self) {
eprintln!("1");
if let List::Nil = *self { return }
eprintln!("2");
let mut head = mem::replace(&mut *self, List::Nil);
eprintln!("3");
while let List::More(ref mut node) = mem::replace(&mut head, List::Nil) {
eprintln!("4");
head = mem::replace(node, List::Nil);
eprintln!("5");
}
eprintln!("6");
}
1
2
3
4
1
5
1
2
3
4
1
5
The problem now is that when we re-assign head, the value we are overwriting needs to be dropped, which triggers the recursion again.
Fixing this is complicated. Like, surprisingly so. You ready for this?
impl Drop for List {
fn drop(&mut self) {
match *self {
List::Nil => return,
List::More(ref more) => {
if let List::Nil = **more {
return;
}
}
}
let mut head = mem::replace(self, List::Nil);
while let List::More(ref mut next) = {head} {
head = mem::replace(next, List::Nil);
}
}
}
This now has two stopping conditions:
Nil
More(Nil)
In every other case, we iteratively convert the More(x) into a More(Nil), which is handled by the stopping condition. That means that we only have a single depth of recursion: for each value that is dropped when the previous value of head goes out of scope when it is replaced.
For your original code:
impl Drop for List {
fn drop(&mut self) {
match *self {
List::Nil => return,
List::More(ref more) => {
if let List::Nil = more.next {
return;
}
}
}
let mut head = mem::replace(self, List::Nil);
while let List::More(ref mut node) = {head} {
head = mem::replace(&mut node.next, List::Nil);
}
}
}
In the original tutorial you linked, this isn't a problem because the definition of List::drop doesn't modify self at all so it's not self-recursive:
impl Drop for List {
fn drop(&mut self) {
let mut cur_link = mem::replace(&mut self.head, Link::Empty);
while let Link::More(mut boxed_node) = cur_link {
cur_link = mem::replace(&mut boxed_node.next, Link::Empty);
}
}
}
You are getting a stack overflow because your drop function is infinitely recursive.
The code below:
let mut head = mem::replace(self, List::Nil);
Stores a List object in head, which will be dropped at the end of the scope. This means that, while you are dropping, you create a new list which also needs to be dropped. Repeat this enough times and you get a stack overflow.
Note: in the tutorial you linked, they use let mut cur_link = mem::replace(&mut self.head, Link::Empty) to avoid recursion.
Disclaimer: i'm a half Rx newbie, so it is very possible that the idea is completely bonkers :)
I'm trying to write ObservableType filter, which would pass only certain type, but will pass that type, not the original sequence type. This is what i came up with so far:
extension ObservableType where Self.E: RxFilterableType {
func filterByCast<T: RxFilterableType>(class: T.Type) -> Observable<T> {
let retval = PublishSubject<T>()
self.subscribe { event in
switch event {
case .next(let element):
if let passed = element as? T {
retval.onNext(passed)
}
case .error(let error):
retval.onError(error)
case .completed:
retval.onCompleted()
}
}
return retval
}
}
func test() {
class A: RxFilterableType {}
class B: RxFilterableType {}
let array: [RxFilterableType] = [A(), B()]
let observable: Observable<RxFilterableType> = Observable.from(array)
let observableCasted: Observable<A> = observable.filterByCast(class: A.self)
}
This has two problems: the lesser problem is that the inner subscribe disposable is not taken care of. Ideally i'd like to pass the disposal responsibility onto the return value, but i can take the disposer as parameter. I don't care.
The bigger problem is the compiler objection on the last test line:
Using 'RxFilterableType' as a concrete type conforming to protocol 'RxFilterableType' is not supported
Which means, i'm afraid, that the compiler has not enough informations to infer what i'm trying to do, despite more-than-necessary hints i've added in desperate attempts to help the poor guy.
If you put this in a playground configured to use RxSwift, it will work:
import RxSwift
extension ObservableType {
func filterByCast<T>() -> Observable<T> {
return self.filter { $0 is T }.map { $0 as! T }
}
}
protocol Foo { }
struct A: Foo { }
struct B: Foo { }
let array: [Foo] = [A(), B()]
let observable = Observable.from(array)
let casted: Observable<A> = observable.filterByCast()
_ = casted.subscribe(onNext: { print($0) })
Or if you don't like specifying the type of casted:
extension ObservableType {
func filterByCast<T>(_ class: T.Type) -> Observable<T> {
return self.filter { $0 is T }.map { $0 as! T }
}
}
protocol Foo { }
struct A: Foo { }
struct B: Foo { }
let array: [Foo] = [A(), B()]
let observable = Observable.from(array)
let casted = observable.filterByCast(A.self)
_ = casted.subscribe(onNext: { print($0) })
Requiring the class type as a parameter is a nice touch of yours. I hadn't thought of doing that.
In swift 3,the method is show me "ambiguous use of 'enumerateObjects'",what happen.how can i do?
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects { (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
}
return list
}
}
Swift 3.0: Just add the Round Brackets before Curly Brackets starts after enumerateObjects.
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects ({ (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
})
return list
}
}
Do something like this noh. You can't directly add extension for PHFetchResult because it has other ObjectType as its generic parameter PHFetchResult<ObjectType> . So you must do something else.
class FetchPhoto {
class func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
PHAssetCollection.fetchMoments(with: nil).enumerateObjects(EnumerationOptions.concurrent) { (collection, _, _) in
list.append(collection)
}
return list
}
}
PHAssetCollection.fetchMoments returns PHFetchResult<PHAssetCollection> here PHAssetCollection is the ObjectType for the PHFetchResult. You got the ambiguous error because you have not specified the objectType.
A generic way to approach this.
class FetchPhoto {
class func assetCollection<T : PHObject>(result : PHFetchResult<T>) -> [T] {
var list : [T] = []
result.enumerateObjects(EnumerationOptions.concurrent) { (object, _, _) in
list.append(object)
}
return list
}
}
Swift 3
class PhotosHelper {
class func fetchAllLocalIdentifiersOfPhotos(completion : (_ localIdentifiers : [String]) -> ()) {
let photos : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
photos.enumerateObjects ({ _,_,_ in
// Do your operations, you can see that there is no warnings/errors in this one
})
}
}