Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the Element and Ring structs for golang list/ring?

Tags:

list

go

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.

like image 280
cellige Avatar asked Mar 20 '23 07:03

cellige


2 Answers

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
}
like image 155
nos Avatar answered Mar 22 '23 00:03

nos


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.

like image 33
peterSO Avatar answered Mar 21 '23 22:03

peterSO