Open jonbarrow opened 6 months ago
you can always cast to the primitive type and do a normal
==
comparison
Not in types which take in an RVType
, like List
and Map
. These will always use the Equals
method, we don't know if it's a basic type alias or not
Also it would allow for code like u32.Equals(5)
, removing the need for a type cast and possible pointer dereference. Mostly a convenience thing, though yes not strictly necessary for type aliases
However it WOULD allow for us to use .Equals
on non-primitive types like the structs in the protocols lib whether or not it's a pointer or not (right now it requires a pointer to the object so we have to make sure we manage that)
IIRC using reflect will not initialize the internal pointers of the struct
In my testing it did, but this can be done. I'll play around with it tomorrow unless you beat me to it
IIRC using reflect will not initialize the internal pointers of the struct, so I believe using type aliases for List and Map would not be feasible (and it would require more rebasing anyway).
I just tested this and it does create a new pointer to the struct:
package main
import (
"fmt"
"reflect"
)
type RVType interface {
Extract()
Copy() RVType
}
type Struct struct {
field string
}
func (s *Struct) Extract() {}
func (s Struct) Copy() RVType {
return &Struct{field: s.field}
}
type List[T RVType] struct {
real []T
}
func (l List[T]) newType() T {
var t T
tType := reflect.TypeOf(t).Elem()
return reflect.New(tType).Interface().(T)
}
func (l List[T]) test() {
t := l.newType()
fmt.Printf("%T\n", t)
}
func main() {
list := List[*Struct]{
real: make([]*Struct, 0),
}
list.test() // *main.Struct
}
In this example t
in the test
method is a pointer to Struct
. With this we can do a Copy
or implement a new method like New
to initialize anything inside the struct
I meant the fields inside the struct, which are pointers and would be assigned as nil. I believe these fields would have to be pointers so that we can use ExtractFrom
?
I meant the fields inside the struct
This is why I proposed adding a new method such as New
to RVType
which would initialize anything like that
I believe these fields would have to be pointers so that we can use
ExtractFrom
?
ExtractFrom
is a pointer receiver but it does not need a pointer at the time of calling:
package main
import (
"fmt"
)
type RVType interface {
ExtractFrom()
}
type Struct struct {
field string
}
func (s *Struct) ExtractFrom() {
s.field = "test"
}
func main() {
s := Struct{}
s.ExtractFrom()
fmt.Printf("%+v\n", s) // {field:test}
}
That being said, even if it did:
Map
to work in this caseExtractFrom
call on those fields happens inside the parent's ExtractFrom
, we could always just call it on a pointer to those fields (&g.ID).ExtractFrom(readable)
Addresses concerns mentioned in #50
Changes:
Makes large changes to how we handle types, both in terms of the API and internally. In https://github.com/PretendoNetwork/nex-go/pull/42 we made Go's built in types into our own types, but all the types we made were structs. This includes simple types, like the built in ones, which only had a single field to hold the "real" value. This was caused by a misunderstanding of the type system and how pointer receivers worked with interfaces/generics, deeming type aliases nonviable.
This was incorrect, type aliases ARE viable. PR makes the following changes:
Primitive
types to just their type names. AddingPrimitive
was both a mouthful and unnecessaryList
andMap
into type aliasesMap
KEYS MUST NOW IMPLEMENT THEcomparable
TYPE CONSTRAINT. THIS MEANSMap
ANDList
THEMSELVES CANNOT BE KEYS, NOR CAN *ANY* TYPE WHICH HAS THOSE AS FIELDS ANYWHERE IN ITS TREEStationURL
to be more accurately implemented in terms of the APIStationURL
to prevent badly formatted URLs from reaching clientsStationURL
. This is not used by NEX, but is used by Rendez-Vous titles like WATCH_DOGSNew
methods for types no longer return pointers by defaultensureFields
method existing on complex types that may need to ensure some fields are initialized before use. This is required for using complex types inMap
andList
typesThis allows for a more streamlined way of interacting with these types, allowing for code such as:
Where our custom types can be used more-or-less like built in types, rather than using an arbitrary
Value
field or method, and needing to export many methods for basic operations like bitwise on our numeric types.This PR DOES NOT:
Equals
methods to accept anything other than a pointer to the same typeMake theIt does nowList
type into a type aliasMake theIt does nowMap
type into a type aliasThe
Equals
method issue can be resolved, by changing the signature toEquals(o any) bool
, and then checking for more types. This just requires some extra checks, and I'm not sure how useful it would be? With type aliases on simple types we can just use the==
operator now, rather thanEquals
(which is now really only useful when you JUST have anRVType
and don't know its type?)Edit:
Map
andList
are now type aliases as wellTheList
andMap
types not being type aliases also means that the original issues described in #50 are still relevant. However these CAN viably be made into type aliases, but come with some caveats which are going to require more changes across the other libraries.The
List
type can somewhat easily be turned into a type alias like so:However this has 2 caveats:
Type
field. Which means we can no longer use it to create new instances of theList
element type. However there is a way around this, which is mentioned laterThe
Map
type is a bit different. To make it into a type alias, we have to use themap
basic type. This type REQUIRES that the key type implements thecomparable
constraint. What this means is that the type can be evaluated with the==
operator, which isn't always guaranteed for custom types depending on how they're implementedGiven that, the
Map
type can also be made into a type alias like so:However this has 3 caveats:
Map
element types. However there is a way around this, which is mentioned laterMap
values MUST be a pointer, not the raw value. We already do this, but it's important to keep in mindMap
key MUST be able to safely be evaluated with the==
operator, which currently NONE of our struct-based types are. Our type alias types ARE, so types such asUInt32
can safely be used, but ONLY when NOT used as pointers. SoMap[UInt32, *Something]
would work, butMap[*UInt32, *Something]
would NOT work.We CAN get around the 3rd issue, however. Struct-based types WILL evaluate safely with
==
IF they contain the same data:However to do this it means that NONE of the structs fields can be pointers, they MUST be whole values. Which means we would need to completely change how we manage types in https://github.com/PretendoNetwork/nex-protocols-go as well as everywhere else. This IS viable, but comes with the following caveats:
Making the
List
andMap
types would potentially address/fix the issues mentioned in #50, but would need more of a rework in other libraries.To get around the missing
List
andMap
element types, we can usereflect
for this. In the past we have avoided reflection when possible as it can be slow and error prone. However I ran several benchmarks against usingreflect
with a test implementation ofList
and found no noticeable difference in performance. The code for this would look something like: