I have a number of structs that share some common properties and logic, but I haven't been able to find a way to share logic that works. Basically, I have a file as part of a package that handles "widgets". I wanted to have a general Widget struct that's embedded in each specified widget struct WidgetA and WidgetB, then share a bit of logic during the creation process. For example:
type Widget struct {
name string
}
type WidgetA struct {
Widget
inspector string
}
type WidgetB struct {
Widget
length int
height int
}
func (w *Widget) Init() {
// Do some shared things
// Then figure out which _type_ of Widget this is and delegate
// to the appropriate receiver function
}
func (wa *WidgetA) Create () { ... }
func (wb *WidgetB) Create () { ... }
On the calling end, maybe something like this:
widgetA := &WidgetA{}
widgetA.Init()
I can't find a working solution to share the logic in Go, so I'd like to either understand where I'm going wrong or, if I'm going wrong be trying to apply old habits where they don't belong, figure out the most idiomatic way of accomplishing the same goal using Go.
Insight appreciated!
You can do something like this:
func (w *Widget) Init() {...}
func (wa *WidgetA) Init() {
wa.Widget.Init()
// init widgetA
}
func (wb *WidgetB) Init() {
wb.Widget.Init()
// init widgetB
}
widgetA:=WidgetA{}
widgetA.Init()
If you don't know the type of the object you're initializing, you can use an interface:
type Initer interface {
Init()
}
func f(w Initer) {
w.Init() // Call the Init() for the underlying widget type
}
Related
I'm trying to extend fyne widget to have a simple clickable content with background.
I searched fyne widgets to find an example that could be used as a starter and found something similar in List/ListItem.
I basically copied the list item code and adapted it a little bit.
It does look similar to the simpler example on fyne documentation.
But for some unknown reason go gives me an error and I don't know what the cause is or how I can fix it:
custom_widget/simple_card.go:80:24: c.card.super undefined (type *SimpleCard has no field or method super)
Here is the code of the module (custom_widget/simple_card.go):
package custom_widget
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"log"
)
// Declare conformity with interfaces.
var _ fyne.Widget = (*SimpleCard)(nil)
var _ fyne.Tappable = (*SimpleCard)(nil)
type SimpleCard struct {
widget.BaseWidget
onTapped func()
background *canvas.Rectangle
content fyne.CanvasObject
selected bool
}
func NewSimpleCard(content fyne.CanvasObject, tapped func()) *SimpleCard {
card := &SimpleCard{onTapped: tapped, content: content}
card.ExtendBaseWidget(card)
return card
}
// CreateRenderer is a private method to Fyne which links this custom_widget to its renderer.
func (c *SimpleCard) CreateRenderer() fyne.WidgetRenderer {
c.ExtendBaseWidget(c)
c.background = canvas.NewRectangle(theme.SelectionColor())
c.background.Hide()
objects := []fyne.CanvasObject{c.background, c.content}
// NewBaseRenderer and BaseRenderer are copied from
// https://github.com/fyne-io/fyne/blob/master/internal/widget/base_renderer.go
// because the functionality is marked internal in fyne !?
return &SimpleCardRenderer{NewBaseRenderer(objects), c}
}
func (c *SimpleCard) Tapped(_ *fyne.PointEvent) {
log.Println("I have been tapped")
if c.onTapped != nil {
c.selected = true
c.Refresh()
c.onTapped()
}
}
// Declare conformity with the WidgetRenderer interface.
var _ fyne.WidgetRenderer = (*SimpleCardRenderer)(nil)
type SimpleCardRenderer struct {
BaseRenderer
card *SimpleCard
}
// MinSize calculates the minimum size of a SimpleCardRenderer.
// This is based on the size of the status indicator and the size of the child object.
func (c *SimpleCardRenderer) MinSize() fyne.Size {
return c.card.content.MinSize()
}
// Layout the components of the SimpleCardRenderer custom_widget.
func (c *SimpleCardRenderer) Layout(size fyne.Size) {
c.card.background.Resize(size)
c.card.content.Resize(size)
}
func (c *SimpleCardRenderer) Refresh() {
if c.card.selected {
c.card.background.FillColor = theme.SelectionColor()
c.card.background.Show()
} else {
c.card.background.Hide()
}
c.card.background.Refresh()
canvas.Refresh(c.card.super()) // compiler error !
}
Remove all of the renderer type you created and in the CreateRenderer just return widget.NewSimpleRenderer(container .NewMax(c.background, c.content)). It is simpler than you think.
Copying code out of the main widgets is often not the best way as we have shortcuts and/or must support more functionality than your own widgets.
I used the github.com/hooklift/gowsdl/soap , and I got generated code with this pattern
package funky
import (
"fmt"
)
type Place struct {
Text string
}
type RandomFunky interface {
Buggy(b int)(int)
}
type randomFunky struct {
place *Place
}
func NewFunky(p *Place) RandomFunky {
return &randomFunky{
place: p,
}
return nil
}
func (rf * randomFunky) Buggy(b int)(int) {
fmt.Println(rf.place.Text)
return b
}
package main
import (
"fmt"
"funky"
)
func main() {
p := funky.Place{}
p.Text = "o jee"
f := funky.NewFunky(&p)
fmt.Printf("%T\n",f) // type of "*funky.randomFunky"
// var f *funky.randomFunky !!! cannot refer to unexported name funky.randomFunky
f.Buggy(1)
}
"f" - I can get this type this way, but I can not create a var of this type!
What if I want to store *funky.randomFunky var in the struct ?
Or pass it to the function?
Is this implies that I always have to recreate NewFunky every time I want to call Buggy method?
(assuming Buggy called in multiple places and/or interface has more methods)
Or should I modify the code and make *funky.randomFunky public?
Or else?
So the idea is that any variable/function/type/method starting with lowercase is local, and starting with uppercase is global. Local stuff can be used directly only inside the same package.
So in your case, RandomFunky is a global interface with a single method Buggy, and a randomFunky is a local structure, implementing RandomFunky, because it has the method Buggy with the same parameters.
In fact, NewFunky returns you a structure wrapped into an interface. So you can't use randomFunky outside the package funky, but you can use funky.RandomFunky.
In your case you can just create a variable of this type
...
var f funky.RandomFunky
f = funky.NewFunky(&p)
...
What would be the go-way of partly providing a default implementation?
To illustrate, the following simple example of a toggle switch driver is the dead-end I ended with by following my OO intuition... Of course it does not compile (I know why) and I am not necessarily willing to do so. Any other solution better fitting into the go philosophy would be in fact even better to correctly understand the go-way for this common need.
The complete example can also be found at https://play.golang.org/p/MYED1PB-dS
Given the following interface:
type ToggleSwitch interface {
TurnOn()
TurnOff()
IsOn() bool
Toggle()
}
Toggle() is a good candidate to be provided a default implementation (ie, according to the current state, turn on or off the switch):
// The Toggle() method can already be defined using TurnOn, TurnOff() and IsOn().
type DefaultDriver struct {
}
// The following implementation would be fine for non-optimized cases:
func (d *DefaultDriver) Toggle() {
state := d.IsOn()
fmt.Println("generic toogle ->", state)
if state {
d.TurnOff()
} else {
d.TurnOn()
}
}
And then an actual driver could use it or not:
// Example of an actual ToggleDriver which should fully implement the interface
// based on the default implementation or not.
// For example, if the toggle switch device has a bult-in toggle command, the
// Toggle() method could be optimized to directly use it.
type DummyDriver struct {
*DefaultDriver // promote DefaultDriver methods
state bool
}
func (d *DummyDriver) IsOn() bool {
return d.state
}
func (d *DummyDriver) TurnOff() {
d.state = false
}
func (d *DummyDriver) TurnOn() {
d.state = false
}
// Uncomment me to optimize me...
//func (d *DummyDriver) Toggle() {
// fmt.Println("specialized toogle ->", d.state)
// d.state = !d.state
//}
Personnally, I would implement the Toggle, IsOn, TurnOn and TurnOff methods for the DefaultDriver type, so it would satisfy the ToggleSwitch interface.
Then, the DummyDriver type would embed the DefaultDriver type.
This way, you could implement the specialized methods for the DummyDriver type, as you desire.
The result would be something along these lines:
package main
import "fmt"
type ToggleSwitch interface {
TurnOn()
TurnOff()
IsOn() bool
Toggle()
}
type DefaultDriver struct {
state bool
}
func (d *DefaultDriver) Toggle() {
state := d.IsOn()
fmt.Println("generic toogle ->", state)
if state {
d.TurnOff()
} else {
d.TurnOn()
}
}
func (d *DefaultDriver) IsOn() bool {
return d.state
}
func (d *DefaultDriver) TurnOff() {
d.state = false
}
func (d *DefaultDriver) TurnOn() {
d.state = true
}
type DummyDriver struct {
DefaultDriver
state bool
}
// Uncomment me to optimize me...
//func (d *DummyDriver) Toggle() {
// fmt.Println("specialized toogle ->", d.state)
// d.state = !d.state
//}
func main() {
d := DummyDriver{state: false}
d.Toggle()
d.Toggle()
d.Toggle()
}
https://play.golang.org/p/Xm-8A0xoRb
In my opinion what you're trying to do isn't very Go-like.
ToggleSwitch defines four methods that clearly work on some state. In order to provide an implementation for any of these methods, you also need to implement that state (even if that state's non-existent, e.g. by defining those methods as no-ops), but at that point, it simply doesn't make sense not to provide all those methods.
With Go-like type composition the embedded types should generally be "whole", complete and useful on their own. Methods provided by the embedded type only work on that field alone, there's no way to get to the "parent" or its methods.
If ToggleSwitch also had other methods that didn't deal with that state, then it'd make sense to provide only a partial implementation of the interface, but at that point an even better and much more idiomatic solution would be to define ToggleSwitch as a combination of two separate interfaces.
In other words, I don't think there's a "Go way" to provide a partial implementation of an interface (that's not composed of several interfaces), because it's idiomatic in Go to define interfaces that are as small as possible.
This is my first question on StackOverflow so please go easy on me.
I've been struggling with getting Swift to invoke the appropriate generic overload.
Suppose I have the following protocol -
protocol MyProtocol { }
And I have the following generic methods -
func foo<T>() -> T
func foo<T: MyProtocol>() -> T
One would expect that invoking foo() with a return type of T conforming to MyProtocol would invoke the appropriate overload.
let bar: MyProtocol = foo()
The above code actually invokes the following function during runtime and Cmd + Click in the IDE navigates to the wrong overload as well.
func foo<T>() -> T
For some reason I cannot get this to work properly in Xcode 7.1.1.
Am I missing something completely fundamental here or is this another Swift quirk?
EDIT
Adding an example of this behavior in action as per matt's request.
protocol MyProtocol { }
class MyProtoClass : MyProtocol { }
class Bar {
func foo<T>(value: T) {
print("T is Generic")
}
func foo(value: MyProtocol) {
print("T conforms to MyProtocol")
}
}
class MyClass<T> {
var value: T
init(value: T) { self.value = value }
var b = Bar()
func print() {
b.foo(value)
}
}
MyClass<MyProtocol>(value: MyProtoClass()).print()
MyClass<String>(value: "").print()
Copying and pasting the above code into a Swift command line application and executing yields the following output.
T is Generic
T is Generic
I think the problem here is that protocols in generics (and generally in Swift) don't work the way you want them to. They are not acting as first-class types. I know that's rather vague... but look at it this way; if you eliminate the func foo<T>(value: T) version of foo, your code won't even compile. In other words, Swift isn't making a choice of foo and choosing wrong; it's saying that b.foo(a1.value) does not call func foo<T: MyProtocol>(value: T).
I have a vague feeling that this is related to my question here:
Protocol doesn't conform to itself?
Okay, I am going to answer my own question here.
After some investigation it seems that Swift wants you to implement an extension with a type constraint on the generic parameter.
extension MyClass where T : MyProtocol {
func print() {
b.foo(value)
}
}
I know this doesn't really solve the problem but it was sufficient enough for me as a work around in my real world use case.
The above sample would wind up looking something like the following.
protocol MyProtocol { }
class MyProtoClass : MyProtocol { }
class Bar {
func foo<T>(value: T) {
print("T is Generic")
}
func foo(value: MyProtocol) {
print("T conforms to MyProtocol")
}
}
class MyClass<T> {
var value: T
init(value: T) { self.value = value }
var b = Bar()
func print() {
b.foo(value)
}
}
extension MyClass where T : MyProtocol {
func print() {
b.foo(value)
}
}
MyClass<MyProtoClass>(value: MyProtoClass()).print()
MyClass<String>(value: "").print()
When compiling the following Swift code (in Sample.swift):
import Cocoa
class Sample {
func doSomething() {
var stringArray = Array<String>()
stringArray.append("AAA")
addToString(stringArray)
stringArray.append("CCC")
}
func addToString(myArray:Array<String>) {
myArray.append("BBB")
}
}
I get the following error on the line 'myArray.append("BBB")':
Immutable value of type 'Array<String>' only has mutating members named 'append'
How do I fix the code to allow a call to this mutable method?
Many thanks in advance
If you want to modify the array you have to specify it as in an inout parameter like this func addToString(inout myArray:Array<String>). Then when you call the function you have to add & in front of your argument to show that it can be modified by the function. Your code will look something like this:
class Sample {
func doSomething() {
var stringArray = Array<String>()
stringArray.append("AAA")
addToString(&stringArray)
stringArray.append("CCC")
}
func addToString(inout myArray:Array<String>) {
myArray.append("BBB")
}
}
You may want to take a look at in-out parameters on this page.