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  }