Вручную применить / разложить chan на <-chan - PullRequest
0 голосов
/ 06 августа 2020

У меня есть функция:

func f(input interface{}) interface{} {
    t := reflect.ChanOf(reflect.BothDir, <type that depends on input>)
    c := reflect.MakeChan(t, ...)

    // ... push things from input into c ...

    return c
}

Она создает канал с типом элемента, который зависит от ввода. Его можно было бы использовать так, но я хочу, чтобы он работал для любого типа, а не только для int:

x := f(1).(<-chan int)

Это не удается, потому что мне нужно использовать reflection.BothDir внутри функции ( потому что я отправляю ему значения внутри f), поэтому фактический тип - chan <something>. Я хочу, чтобы пользователи этой функции видели только принимающую сторону канала. Есть ли способ «преобразовать» chan <something> в <-chan <something> перед возвратом из f? Использование возвращаемого типа <-chan interface{} не является вариантом, поскольку для перехода от этого типа к <-chan <something> потребуется, чтобы пользователь f создал другой канал и вручную sh значения в нем, но я хочу f для использования в качестве однострочного.

РЕДАКТИРОВАТЬ:

Я также попытался сделать это как можно более общим, но я думаю, что должен быть подробнее c:

Я пытаюсь написать функцию, которая принимает фрагмент значений типа A и возвращает канал, заполненный значениями этого фрагмента. Он предназначен для использования в модульных тестах, поэтому меня не волнует необходимость использования отражения, но я действительно забочусь о том, чтобы тесты были как можно более краткими, что в моем конкретном случае означает рефакторинг кода инициализации, совместно используемого несколькими тестами. Точный код:

func gen(input interface{}) interface{} {
    xs := reflect.ValueOf(input)

    chanType := reflect.ChanOf(reflect.BothDir, xs.Type().Elem())
    out := reflect.MakeChan(chanType, 0)

    go func() {
        for i := 0; i < xs.Len(); i++ {
            x := xs.Index(i)
            out.Send(i)
        }

        out.Close()
    }

    return out.Interface()
}

Теперь, если я хочу протестировать функцию g(<-chan int), где раньше мне приходилось писать

values := <input generated by quick.check>

in := make(chan int)
go func() {
    for _, value := range values {
        in <- value
    }
    close(in)
}()
g(in)

, я действительно могу теперь просто позвонить

g(gen(values).(chan int))

и работает нормально. Это связано с тем, что тип возврата gen равен chan int, который неявно будет преобразован в <-chan int. Мой вопрос касался такого случая:

o := gen([]int{1, 1, 2, 3}).(chan int)
o <- 5 // You're not supposed to do that, sending to o is exclusive to the inside of the gen function
g(o)

Я признаю, что этот случай очень нишевый, и я буду в порядке, даже если Go не сможет предотвратить это, но в основном я был интересно, была ли часть языка, которую я пропустил, что позволило бы мне предотвратить вышеуказанный случай.

1 Ответ

0 голосов
/ 06 августа 2020

Без отражения вы использовали бы (возможно, неявное) преобразование типов:

func sliceToChan(xs []int) <-chan int {
    c := make(chan int)
    // ...
    return (<-chan int)(c) // or just `return c` for the implicit conversion
}

С отражением сделайте то же самое, т.е. используйте Convert :

package main

import (
    "fmt"
    "reflect"
)

func main() {
    x := sliceToChan([]int{1}).(<-chan int)
    fmt.Println(cap(x))
}

func sliceToChan(input interface{}) interface{} {
    xs := reflect.ValueOf(input)
    elemType := xs.Type().Elem()

    t := reflect.ChanOf(reflect.BothDir, elemType)
    c := reflect.MakeChan(t, xs.Len())

    for i := 0; i < xs.Len(); i++ {
        x := xs.Index(i)
        c.Send(x)
    }

    c.Close()

    recvOnly := c.Convert(reflect.ChanOf(reflect.RecvDir, elemType))

    return recvOnly.Interface()
}

Попробуйте на детской площадке: https://play.golang.org/p/RDaAA_l9ghN

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...