Suppose we have something like this:
type ReadHandler interface {
Reader
Reader
}
Obviously, it will cause ambiguity when we call members of Reader interface on instances of ReadHandeler. So how can we achieve this in Golang?
1- Use io.MultiReader, see func MultiReader(readers ...Reader) Reader Docs:
MultiReader returns a Reader that's the logical concatenation of the
provided input readers. They're read sequentially. Once all inputs
have returned EOF, Read will return EOF. If any of the readers
return a non-nil, non-EOF error, Read will return that error.
2- or name it:
type ReadHandler interface {
Read(p []byte) (n int, err error)
Read2(p []byte) (n int, err error)
}
or:
type ReadHandler interface {
io.Reader
Read2(p []byte) (n int, err error)
}
Demonstration working sample code:
package main
import (
"errors"
"fmt"
"io"
)
func main() {
s := my{[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []byte{10, 20, 30, 40, 50}}
buf := make([]byte, 5)
n, e := s.Read(buf)
fmt.Println(n, e, buf)
n, e = s.Read2(buf)
fmt.Println(n, e, buf)
}
type ReadHandler interface {
io.Reader
Read2(p []byte) (n int, err error)
}
type my struct {
buf []byte
buf2 []byte
}
func (t *my) Read(p []byte) (n int, err error) {
if len(p) > len(t.buf) {
return 0, errors.New("len(p)>len(buf)")
}
m := copy(p, t.buf)
return m, nil
}
func (t *my) Read2(p []byte) (n int, err error) {
if len(p) > len(t.buf2) {
return 0, errors.New("len(p)>len(buf2)")
}
m := copy(p, t.buf2)
return m, nil
}
Output:
5 <nil> [1 2 3 4 5]
5 <nil> [10 20 30 40 50]
3- name it:
Looking for a general solution to embed an interface (with any number
of methods), twice in a struct.
type my struct {
io.Reader
Rdr2 io.Reader
}
or
type my struct {
Rdrs []io.Reader
}
Related
In Golang, how can I iterate over three slices with a single for loop, without creating a new slice containing copies of the elements in all three slices? Is there something like Python's itertools.chain?
A simple solution using generics
package main
import "fmt"
func Chain[T any](f func(e T), slices ...[]T) {
for _, slice := range slices {
for _, e := range slice {
f(e)
}
}
}
func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{10, 20, 30}
slice3 := []int{100, 200, 300}
Chain(func(e int) {
fmt.Println(e)
}, slice1, slice2, slice3)
}
Output:
1
2
3
10
20
30
100
200
300
The function Chain takes a function parameter, which will be executed for each element in each slice consecutively. This function will serve as your loop body code.
The solution can be extended to retain other looping features such as break and index numbers:
func Chain[T any](f func(i int, e T) bool, slices ...[]T) {
var i int
for _, slice := range slices {
for _, e := range slice {
if !f(i, e) {
return
}
i++
}
}
}
...
Chain(func(i int, e int) bool {
fmt.Println(i, "-", e)
return (e <= 20)
}, slice1, slice2, slice3)
...
The loop will "break" when f returns false.
Output:
0 - 1
1 - 2
2 - 3
3 - 10
4 - 20
5 - 30
You could use channels to build your own chain() method.
package main
import "fmt"
func main() {
slice1 := []int{2, 3, 5, 7, 11, 13}
slice2 := []int{21321, 12313, 213}
slice3 := []int{8987, 988, 675676, 6587686}
for val := range chain(slice1, slice2, slice3) {
fmt.Println(val)
}
}
func chain[T any](slices ...[]T) <-chan T {
channel := make(chan T)
go func() {
for _, slice := range slices {
for _, val := range slice {
channel <- val
}
}
close(channel)
}()
return channel
}
Here the link to Go Playground.
Essentially the idea is to use variadic argument slices which can hold and undetermined number of slices and in the function we then create a channel of the given type return that channel so a caller can retrieve values from it and before we do that we start a Goroutine which will actually send the values over the channel. In order to be able to use range we need to close() the channel once we have looped over all slices.
This uses Generics which are available as of Go 1.18 which has just recently been released.
Edit
As correctly indicated by Zombo you will leak a goroutine if you break in the range loop above as not all values sent to the channel will be retrieved from it.
In order to get a break behavior without leaking a go routine you could pass a predicate function which, when it returns true will close the channel and therefore prevent a leak.
Similarly you could pass callback functions for all kinds of stuff (e.g. filtering) according to your use case.
package main
import "fmt"
func main() {
slice1 := []int{2, 3, 5, 7, 11, 13}
slice2 := []int{21321, 12313, 213}
slice3 := []int{8987, 988, 675676, 6587686}
for val := range chainUntil(func(item int) bool {
return item > 6
}, slice1, slice2, slice3) {
fmt.Println(val)
}
}
func chainUntil[T any](predicate func(item T) bool, slices ...[]T) <-chan T {
channel := make(chan T)
go func() {
for _, slice := range slices {
for _, val := range slice {
if predicate(val) == true {
close(channel)
return
}
channel <- val
}
}
close(channel)
}()
return channel
}
Expected output:
2
3
5
I'm struggling a bit with this piece of Go code. I have been searching all over the place, but can't understand what is wrong about it.
Error message is: syntax error: unexpected int at end of statement
for that line near the bottom: func (TOHLCV TOHLCVs) Len() int {
I also have this error message for the second to the last line of code:
syntax error: non-declaration statement outside function body
In case the 2 errors are related
package main
import (
"fmt"
"time"
"strconv"
//from https://github.com/pplcc/plotext/
"log"
"os"
"github.com/360EntSecGroup-Skylar/excelize"
"github.com/pplcc/plotext/custplotter"
"gonum.org/v1/plot"
"github.com/pplcc/plotext"
"gonum.org/v1/plot/vg/vgimg"
"gonum.org/v1/plot/vg/draw"
)
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
func main() {
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
}
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
this code is adapted from:
https://github.com/pplcc/plotext/blob/master/custplotter/tohlcv.go
Function declarations need to be moved out of other functions, Like this:
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
}
func main() {
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
}
Type declarations can be inside of a function. But, in this case, it makes more sense for them to be outside. There are some situations where it's helpful to declare a function inside another function:
Passing a function as an argument: https://play.golang.org/p/4NgeUvsexto
Assigning an anonymous function to a variable: https://play.golang.org/p/r1DF9_iP0-k
(I'm not sure about the exact logic you're looking for - the above code doesn't do anything yet. I'll also caution against creating an interface unless you needed it.)
So based on answer of #Tyler Bui-Palsulich and #aec my code now looks like below, and no more error messages :-), thanks all !
package main
import (
"fmt"
"time"
"strconv"
//from https://github.com/pplcc/plotext/
"log"
"os"
"github.com/360EntSecGroup-Skylar/excelize"
"github.com/pplcc/plotext/custplotter"
//"github.com/pplcc/plotext/examples"
"gonum.org/v1/plot"
"github.com/pplcc/plotext"
"gonum.org/v1/plot/vg/vgimg"
"gonum.org/v1/plot/vg/draw"
)
// Len implements the Len method of the TOHLCVer interface.
//func (TOHLCV TOHLCVs) Len() int {
// return len(TOHLCV)
//}
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
}
// TOHLCV implements the TOHLCV method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) TOHLCV(i int) (float64, float64, float64, float64, float64, float64) {
return TOHLCV[i].T, TOHLCV[i].O, TOHLCV[i].H, TOHLCV[i].L, TOHLCV[i].C, TOHLCV[i].V
}
func main() {
start := time.Now()
//create data for each chart****************************************************
//******************************************************************************
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/hugues/M.2 windows/Hugues/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
for _, row := range df[1:61] { // read 60 rows
df3[idx].T, err = strconv.ParseFloat(row[28], 64)
df3[idx].O, err = strconv.ParseFloat(row[29], 64)
df3[idx].H, err = strconv.ParseFloat(row[30], 64)
df3[idx].L, err = strconv.ParseFloat(row[31], 64)
df3[idx].C, err = strconv.ParseFloat(row[32], 64)
df3[idx].V, err = strconv.ParseFloat(row[33], 64)
idx++
}
I have the following code to double the slice.
func doubleSlice(s []int) []int {
t := make([]int, len(s), (cap(s) + 1) * 2 )
for i := range s {
t[i] = s[i]
}
return t
}
I want to make the func to double any type of slice. And I need to know the element type first.
func showInterfaceItem(s interface{}) interface{} {
if reflect.TypeOf(s).Kind() != reflect.Slice {
fmt.Println("The interface is not a slice.")
return
}
var t interface{}
newLen := reflect.ValueOf(s).Len()
newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
t = make([]reflect.TypeOf(s), newLen, newCap)
return t
}
The reflect.TypeOf(s) return the type of interface{}, not the type of element. How can I get the element type of slice interface?
You can use reflect.TypeOf(s).Elem()
to get the type of element of slice.
package main
import (
"fmt"
"reflect"
)
func doubleSlice(s interface{}) interface{} {
if reflect.TypeOf(s).Kind() != reflect.Slice {
fmt.Println("The interface is not a slice.")
return nil
}
v := reflect.ValueOf(s)
newLen := v.Len()
newCap := (v.Cap() + 1) * 2
typ := reflect.TypeOf(s).Elem()
t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
reflect.Copy(t, v)
return t.Interface()
}
func main() {
xs := doubleSlice([]string{"foo", "bar"}).([]string)
fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
ys := doubleSlice([]int{3, 1, 4}).([]int)
fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
}
The output will be:
data = [foo bar] len = 2 cap = 6
data = [3 1 4] len = 3 cap = 8
Check it in: Go Playground
This is doable in golang and takes me whole day to discover the pattern.
Firstly, we want to get a pointer of slice to make gorm happy, which is has type "*[]Obj". To achieve that in golang, we can create a make wrapper like so:
func makeWrapper(cap uint) interface{} {
arr:= make([]Sth, 0, cap)
return &arr
}
Notice that, we can't directly reference the maked value, which might be the book keeping data need to have a stack space to store.
//Not working example
func makeWrapper(cap uint) interface{} {
return &(make([]Sth, 0, cap))
}
And as the answer before, the reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() returns interface{[]Sth}. (the typ here is refer to reflect.TypeOf(Sth{}), which equiv to typ == reflect.TypeOf(v))
Thus we need to create a return object of *[]Sth and the value inside is a slice []Sth with capacity. After understanding the objective, we can have this code:
package main
import (
"reflect"
)
type Sth struct {
a, b string
}
func main() {
af:= createSlice(Sth{})
arr := makeWrapper(10).(*[]Sth)
println(reflect.TypeOf(arr).String())
// equiv to makeWrapper, but we do it via reflection
arr = af(10).(*[]Sth)
println(reflect.TypeOf(arr).String())
}
func makeWrapper(cap uint) interface{} {
arr:= make([]Sth, 0, cap)
return &arr
}
func createSlice(v interface{}) func(int) interface{} {
var typ reflect.Type
if reflect.ValueOf(v).Kind() == reflect.Ptr {
typ = reflect.ValueOf(v).Elem().Type()
} else if reflect.ValueOf(v).Kind() == reflect.Struct {
typ = reflect.TypeOf(v)
} else {
panic("only support instance of struct or pointer of that instance")
}
return func(capacity int) interface{}{
// create the outer object saves our slice
outerObj:=reflect.New(reflect.SliceOf(typ))
// create the slice and save it to return
outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
// retrive the interface of outer object
return outerObj.Interface()
}
}
This question already has answers here:
Cannot convert []string to []interface {}
(7 answers)
Closed 7 months ago.
I just want to write some code like this:
func (w Writer) WriteVString(strs []string) (int, error) {
return writeV(func(index int, str interface{}) (int, error) {
return w.WriteString(str.(string))
}, strs) // it doesn't work
}
func (w Writer) WriteV(bs [][]byte) (int, error) {
return writeV(func(index int, b interface{}) (int, error) {
return w.Write(b.([]byte))
}, []interface{}{bs...}) // it also can't be compiled
}
type writeFunc func(int, interface{}) (int, error)
func writeV(fn writeFunc, slice []interface{}) (n int, err error) {
var m int
for index, s := range slice {
if m, err = fn(index, s); err != nil {
break
}
n += m
)
return
}
I thought interface{} can represent any type, so []interface can also represent any []type before, now I know I'm wrong, []type is a whole type, can't be considered as []interface{}.
So, can anyone help me how to make this code work, or any other solution?
PS: I know that []byte or string can be converted to one another, but it's not actually my intention, may be there is another type rather than []byte and string.
now I know I'm wrong, []type is a whole type, can't be considered as []interface{}.
Yes, and that is because interface{} is its own type (and not an "alias" for any other type).
As I mention in "what is the meaning of interface{} in golang?" (if v is a interface{} variable):
Beginner gophers are led to believe that “v is of any type”, but that is wrong.
v is not of any type; it is of interface{} type.
The FAQ mentions
they do not have the same representation in memory.
It is necessary to copy the elements individually to the destination slice.
This example converts a slice of int to a slice of interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
Tom L propose this example (in the comments):
package main
import "fmt"
func main() {
x := []string{"a", "b", "c", "d"}
fmt.Printf("%T: %v\n", x, x)
//converting a []string to a []interface{}
y := make([]interface{}, len(x))
for i, v := range x {
y[i] = v
}
fmt.Printf("%T: %v\n", y, y)
//converting a []interface{} to a []string
z := make([]string, len(y))
for i, v := range y {
z[i] = fmt.Sprint(v)
}
fmt.Printf("%T: %v\n", z, z)
}
Create a utility function, like this
func ToGenericArray(arr ...interface{}) []interface{} {
return arr
}
And use it:
func yourfunc(arr []interface{}) {
....
}
yourfunc(ToGenericArray([...]string{"a", "b", "c"}))
IMPORTANT NOTICE: the following will not work
func yourfunc(arr []interface{}) {
....
}
arr:=[...]string{"a", "b", "c"}
yourfunc(ToGenericArray(arr))
With generics, useful with sql package
func toAnyList[T any](input []T) []any{
list := make([]any, len(input))
for i, v := range input {
list[i] = v
}
return list
}
var b [88]byte
n, err := file.Read(b[:])
fmt.Printf("bytes read: %d Bytes: [% x]\n", n, b)
The above prints bytes in hexdecimal
I have a struct like this
type SomeStruct struct {
field1 []byte
field2 []byte
}
someStructInstance := SomeStruct{[249 190 180 217], [29 1 0 0]}
fmt.Println(someStructInstance)
=> {[249 190 180 217] [29 1 0 0]}
But ideally I would like it to print hexdecimal
=> {[f9 be b4 d9] [1d 01 00 00]}
How would I go about that?
I think you will just have to define your own String function on SomeStruct. Here is an example:
package main
import "fmt"
type SomeStruct struct {
field1 []byte
field2 []byte
}
func (s SomeStruct) String() string {
return fmt.Sprintf("{[% x] [% x]}", s.field1, s.field2)
}
func main() {
someStructInstance := SomeStruct{[]byte{249, 190, 180, 217}, []byte{29, 1, 0, 0}}
fmt.Println(someStructInstance)
}
See it in running on the Go Playground: http://play.golang.org/p/eYBa1n33a2
You could use reflection to inspect the struct and print any []bytes that it has.
package main
import (
"fmt"
"reflect"
)
type SomeStruct struct {
field1 []byte
field2 []byte
}
type OtherStruct struct {
intValue int
intSlice []int
byteSlice []byte
}
var typeOfBytes = reflect.TypeOf([]byte(nil))
func printSlicesHex(obj interface{}) {
value := reflect.ValueOf(obj)
typeOfObj := value.Type()
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
if field.Type() == typeOfBytes {
bytes := field.Bytes()
printBytes(typeOfObj.Field(i).Name, bytes)
}
}
}
func printBytes(name string, bytes []byte) {
fmt.Printf("%s: [% x]\n", name, bytes)
}
func main() {
someStructInstance := SomeStruct{[]byte{249, 190, 180, 217}, []byte{29, 1, 0, 0}}
fmt.Println("Printing []bytes in SomeStruct")
printSlicesHex(someStructInstance)
fmt.Println()
otherStruct := OtherStruct{0, []int{0, 1, 2}, []byte{0, 1, 2, 3}}
fmt.Println("Printing []bytes in OtherStruct")
printSlicesHex(otherStruct)
}
For each []byte, this example prints the name of the field and its data (in hex). You could improve on this by taking a custom function to do the printing, so you don't always have to print in hex.
Playground Link