Why do the list/ring types in golang use the extra structs Element/Ring for the individual items and not interface{} ? I am assuming there is some benefit but I cannot see it.
Edit: I meant to ask about the api and NOT about the use of Element/Ring in the implementation. The implementation could still use a non exported type but have the api give and take an interface{}, so why make the users go in and out of Element/Ring?
Edit2: As an example the list Back() function could be like
func (l *List) Back() interface{} {
if l.len == 0 {
return nil
}
return l.root.prev.Value
}
Where the list still uses Element internally but it would be just element (unexported) since it wouldn't return it but only return the value.
container/list is linked list, so it'll be beneficial to have List
struct that can operate on the list as a whole and keep track of the beginning and end of a list.
Since it's a linked list, you want to be able to link items together and navigate from one item to the next or the previous item. That requires a struct that hold pointers to the next and the previous item as well as allowing you to navigate to those items (with the Next() and Prev() functions). The Element
struct serves that purpose, it contains pointers to the next/previous item, and the actual value.
Here's how the struct's are defined, and they have various member functions as well
type List struct {
root Element // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element
// The list to which this element belongs.
list *List
// The value stored with this element.
Value interface{}
}
container/ring does not have an "extra" struct as you imply. There's only the Ring struct which links one item to the next/previous item and also holds the value. There's no start/end of a Ring, so there's no need to have a struct that operates on a ring as a whole or keeps track of the start.
type Ring struct {
next, prev *Ring
Value interface{} // for use by client; untouched by this library
}
They contain filtered or unexported fields.
Package list
File list.go
:
// Package list implements a doubly linked list.
// Element is an element of a linked list.
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element
// The list to which this element belongs.
list *List
// The value stored with this element.
Value interface{}
}
Package ring
File ring.go
:
// Package ring implements operations on circular lists.
// A Ring is an element of a circular list, or ring.
// Rings do not have a beginning or end; a pointer to any ring element
// serves as reference to the entire ring. Empty rings are represented
// as nil Ring pointers. The zero value for a Ring is a one-element
// ring with a nil Value.
//
type Ring struct {
next, prev *Ring
Value interface{} // for use by client; untouched by this library
}
Obviously, the type of Element
and Ring
can't be interface{}
because it would make no sense. You can't have a method on an interface type.
The Go Programming Language Specification
Method declarations
A method is a function with a receiver. A method declaration binds an identifier, the method name, to a method, and associates the method with the receiver's base type.
MethodDecl = "func" Receiver MethodName ( Function | Signature ) . Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" . BaseTypeName = identifier .
The receiver type must be of the form T or *T where T is a type name. The type denoted by T is called the receiver base type; it must not be a pointer or interface type and it must be declared in the same package as the method. The method is said to be bound to the base type and the method name is visible only within selectors for that type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With