2014-08-31 7 views
8

Sto cercando di scrivere un wrapper di uso generale per gli abbonamenti, qualcosa come:Golang: Posso lanciare all'interfaccia chan {}

type Subscriber interface{ 
    Subscribe(addr string) chan interface{} 
} 

Supponiamo che ci sia una libreria che voglio usare, che ha un metodo sottoscrivere in esso, ma che utilizza un chan library.Object. Vorrei poter fare qualcosa del tipo:

func (s *mySubscriber) Subscribe(addr string) chan interface{}{ 
    ch := make(chan library.Object) 
    library.Subscribe(addr, ch) 
    return chan interface{}(ch) 
} 

Attualmente, non credo che un tale cast sia possibile. E non voglio modificare la libreria sottostante, dal momento che il wrapper dovrebbe essere agnostico alle implementazioni della libreria.

Ho visto Is there a way to cast Structs for sending over a channel, ma in tal caso l'applicazione può essere modificata in base alle esigenze. Qui, non può. È possibile? C'è un modo migliore?

Una soluzione è passare in un canale di uso generale in Iscriviti e attendere indefinitamente su chan library.Object e attivare tutto ciò che viene visualizzato sul mio canale generale, ma non mi è piaciuto particolarmente dover introdurre un altro canale solo per spostarsi il cast del tipo.

+2

Go non ha il concetto di "cast". Vai conversioni di tipo hase, asserzioni di tipo e interfacce (e interruttori di tipo) e il gioco è fatto. Digita le conversioni più vicine al cast, ad es. C. Parlare di cast e cast non funzionanti non farà altro che sfocare la tua visione del problema che deve essere affrontato usando le conversioni di tipo (non funzionerà nella tua situazione!), L'uso intelligente delle interfacce e gli asserzioni/interruttori di tipo appropriato magari combinati con riflessione. La maggior parte della roba "per uso generale" in Go è destinata a fallire o a rallentare con il ginocchio che guada in riflessione. – Volker

risposta

4

No, non puoi farlo solo con un cast. Devi usare un canale extra, come hai già considerato. Fortunatamente, esiste già una libreria di supporto (disclaimer: l'ho scritto). Vuoi la funzione Wrap.

+1

figure. vergogna. forse in futuro gli autori estenderanno il cambio di tipo per supportare questo. con array e mappe abbiamo lo stesso problema, ma almeno lì puoi scorrere gli elementi e digitare switch to interface {} uno per uno. Ci dovrebbe essere un modo per farlo con un canale, ma forse c'è una buona ragione no? – Ethan

+0

Il motivo non è che i tipi hanno layout di memoria fondamentalmente diversi, quindi non è così semplice come un "cast" sarebbe in C. Ad esempio, una [] struct {x, y int} sembra completamente diversa da un [] interfaccia {} quindi non puoi semplicemente "fingere" che sia una, devi fare la conversione manuale. A differenza dei cast semplici, la conversione può essere costosa (come dici tu, devi passare attraverso ogni elemento), quindi hanno deciso di non nasconderlo poiché non è la stessa cosa. – Evan

1

Per chiunque altro che inciampa su questo tema e vuole un po 'di codice inline:

// wrap a timeout channel in a generic interface channel 
func makeDefaultTimeoutChan() <-chan interface{} { 
    channel := make(chan interface{}) 
    go func() { 
    <-time.After(30 * time.Second) 
    channel <- struct{}{} 
    }() 
    return channel 
} 

// usage 
func main() { 
    resultChannel := doOtherThingReturningAsync() 
    cancel := makeDefaultTimeoutChan() 
    select { 
    case <-cancel: 
     fmt.Println("cancelled!") 
    case results := <-resultChannel: 
     fmt.Printf("got result: %#v\n", results) 
    } 
}