github.com/haraldrudell/parl@v0.4.176/iters/slice-interface.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package iters 7 8 import ( 9 "fmt" 10 11 "github.com/haraldrudell/parl/perrors" 12 ) 13 14 type SliceInterface[I any, E any] struct { 15 SlicePointer[E] 16 } 17 18 // NewSliceInterfaceIterator returns an iterator over slice []E returning those elements 19 // as interface I values 20 // - [I ~*E, E any] as an expression of interface I implemented by &E 21 // does not actually hold true at compile time. 22 // The check whether I is an interface implemented by &E 23 // has to take place at runtime 24 // - uses self-referencing pointers 25 func NewSliceInterfaceIterator[I any, E any](slice []E) (iterator Iterator[I]) { 26 27 // check (&E).(I) type assertion 28 // - I should be an interface type 29 // - E should be a concrete type implementing the I interface 30 var ep *E 31 var ok bool 32 if _, ok = any(ep).(I); !ok { 33 var ip *I 34 var iType = fmt.Sprintf("%T", ip)[1:] // get the interface type name by removing leading star 35 var e E 36 var eType = fmt.Sprintf("%T", e) 37 panic(perrors.ErrorfPF("I type: %s is not an interface that &E implements. E type: %s", 38 iType, eType, 39 )) 40 } 41 42 // wrap a slice-pointer iterator in a type assertion shim 43 i := SliceInterface[I, E]{} 44 // the iterator returns *E values 45 NewSlicePointerIteratorField(&i.SlicePointer, slice) 46 return &i 47 } 48 49 // Init initializes I interface values and returns an I iterator 50 // 51 // Usage: 52 // 53 // for i, iterator := NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 54 // // i is pointer to slice element 55 func (i *SliceInterface[I, E]) Init() (iterationVariable I, iterator Iterator[I]) { 56 iterator = i 57 return 58 } 59 60 // Cond updates I interface pointers 61 // 62 // Usage: 63 // 64 // for i, iterator := NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 65 // // i is pointer to slice element 66 func (i *SliceInterface[I, E]) Cond(iterationVariablep *I, errp ...*error) (condition bool) { 67 var ep *E // getting *E but must be type asserted for Go to understand it’s interface I 68 if condition = i.SlicePointer.Cond(&ep, errp...); !condition || ep == nil { 69 var iZeroValue I 70 *iterationVariablep = iZeroValue // assign zero-value 71 return // pointer assigned nil, condition: valid 72 } 73 // ep is not nil, so assertion does not create typed nil 74 *iterationVariablep = any(ep).(I) 75 return // pointer assigned asserted &E, condition: true 76 } 77 78 // Next advances to next item and returns it 79 // - if hasValue true, value contains the next value 80 // - otherwise, no more items exist and value is the data type zero-value 81 func (i *SliceInterface[I, E]) Next() (value I, hasValue bool) { return i.nextSame(IsNext) } 82 83 // Same returns the same value again 84 // - if hasValue true, value is valid 85 // - otherwise, no more items exist and value is the data type zero-value 86 // - If Next or Cond has not been invoked, Same first advances to the first item 87 func (i *SliceInterface[I, E]) Same() (value I, hasValue bool) { return i.nextSame(IsSame) } 88 89 // delegateAction returns I values to the delegator 90 // by type asserting *E values from the slice pointer-iterator 91 func (i *SliceInterface[I, E]) nextSame(isSame NextAction) (value I, hasValue bool) { 92 var ep *E // getting *E but must be type asserted for Go to understand it’s interface I 93 if ep, hasValue = i.SlicePointer.nextSame(isSame); !hasValue || ep == nil { 94 return 95 } 96 97 // here we want value to be *E 98 // - if ep is nil, asserting it will cause a typed nil 99 // - in Go, typed nil does not equal nil, so this must be avoided 100 value = any(ep).(I) // type assertion was checked to not panic in new function 101 102 return 103 }