Polymorphic Clone() using generics?

Trying to create a Clone() function with returns the “final” concrete type and using a reference to polymorphically create a clone of the final type, but hitting compile errors. Ideas/solution appreciated.

Is it possible to make the code below compile or is there an idiomatic way to achieve the polymorphic cloning with embedding of interface/struct ?

package main

import "fmt"

type Cloner[T any] interface {
	Clone() T
}

type AI[T any] interface {
	// Want all implementations to have a Clone() method that returns the final type
	Cloner[AI[T]]
}

// implements the AI interface, by embedding the interface
type AS struct {
	AI[int]
	a int
}

func (as AS) Clone() AS {
	return AS{a: as.a}
}

// implements the AI interface, by embedding the interface
type BS struct {
	AI[int]
	b string
}

func (bs BS) Clone() BS {
	return BS{b: bs.b}
}

// implements the AI interface by embedding AS struct
type CS struct {
	AS
	c int
}

func (cs CS) Clone() CS {
	return CS{
		AS: cs.Clone(), // error: cannot use cs.Clone() (value of type CS) as type AS in struct literal
		c:  cs.c,
	}
}


func main() {
	var aiRef AI[int]

	// try to get a clone of concrete objects

	aiRef = &AS{a: 10}
	fmt.Printf("AS: %#v\n", aiRef.Clone())
	// Compile error:
	//  ./main.go:42:10: cannot use &AS{…} (value of type *AS) as type AI[int] in assignment:
	//  *AS does not implement AI[int] (wrong type for Clone method)
	//  have Clone() AS
	//  want Clone() AI[int]

	aiRef = &BS{b: "hello"}
	fmt.Printf("BS: %#v\n", aiRef.Clone())
	// same compile error as above

	aiRef = &CS{c: 10}
	fmt.Printf("CS: %#v\n", aiRef.Clone())
	// same compile error as above
}

Hi @globalflea,

I just posted an answer to a similar question here. This might also apply to your situation.

Hi,

Thanks, but it did not solve this set of problems. i have still have a problem trying to create an instance of the ‘derived’ type. or rather i don’t know how to declare a definition that will require child struct to have a Clone that returns the child type

specifically, how do i ensure all child to have a clone function?

type AI[T any] interface {
	// Want all implementations to have a Clone() method that returns the final type

	Cloner[AI[T]] 
	// ?? How to declare a generic Clone() to be implemented by children?
	// ??   especially, since i won't know what's the actual child type 
}

think with Rust, i could have declare something like Clone() -> Self

Also in the main(),

	var aiRef AI[int]

	aiRef = &AS{a: 10}
	//   ./main.go:56:10: cannot use &AS{…} (value of type *AS) as type AI[int] in assignment:
	//   *AS does not implement AI[int] (wrong type for Clone method)
	//   have Clone() AS
	//   want Clone() AI[int]

Noted, with thanks that the CloneAny() works for calling the Clone() function for child structs

	// fmt.Printf("AS: %#v\n", aiRef.Clone()) // had compile error earlier
	fmt.Printf("AS: %#v\n", CloneAny(aiRef)) // using CloneAny removed the error

Hey both. Don’t have a solution but would like to share a thought:

how to declare a definition that will require child struct to have a Clone that returns the child type

Is it possible that you’re placing too much emphasis on forcing structs to do something instead of restricting operations to receive certain types?

In other words, instead of forcing child structs to have a clone, why not limit yourself to require some operations to accept Cloner[T]?

func SomethingWithClone[T Cloner[T]](c T) T {
	// ...
	return c.Clone()
}

Forcing types to implement something is the type of thinking I have in languages like Scala and Java, but with Go’s inheritance I tend to focus more on “This operation should accept arguments that are capable of …”


Maybe this isn’t possible?

I don’t know if what you’re trying to do is possible, because methods can’t have type parameters and interfaces are implemented in methods. So requiring a type to have AI[int] seems impossible (at first glance) because the method doesn’t have type parameters.

There’s also something in the comments you shared that I think could be wrong (but not entirely sure, semantically): implements the AI interface, by embedding the interface afaik, interfaces aren’t implemented by embedding unless you’re embedding a struct that already implements it. In this case you’re embedding the interface type directly.

Anyways, this is interesting and I don’t know enough yet, I’m going to read up on the generics thingy to see if some light is shed in my brain.


Sidebar:

I think the Clone in CS:

func (cs CS) Clone() CS {
	return CS{
		AS: cs.Clone(), // error: cannot use cs.Clone() (value of type CS) as type AS in struct literal
		c:  cs.c,
	}
}

Should be:

func (cs CS) Clone() CS {
	return CS{
		AS: cs.AS.Clone(),
		c:  cs.c,
	}
}

And the error there goes away

1 Like