Why are methods created outside the struct?
In languages like C#, you can add the methods inside the struct. I know that in languages like C and C++ you have header files so it makes sense, but as far as I know I can't create header files in Rust.
In most languages, "methods" are just some syntactic sugar. You don't actually have an object and call its methods, you have a function that takes a reference to that object and then does stuff with it. In contrast to regular functions, the reference to the object is passed implicitly by using the dot notation.
struct Foo {
//...
}
impl Foo {
fn do_something(self: &Self) { //"self: &Self" is a more verbose notation for "&self"
//...
}
}
So calling it like this
my_foo.do_something();
Is essentially the same as
Foo::do_something(&my_foo);
I think it's a decision made by the Rust developers to make it more clear that a struct is nothing else than just a set of data.
This is also what allows trait implementation for already existing types.
Related
In the following example of passing a trait as a parameter, what's the need of sending impl in the function signature?
I understand that traits are more generic types and not concrete types, but since the Rust compiler doesn't allow sharing names across structs and traits, why is there a need to provide impl in the function signature to represent the type?
pub fn notify(item: impl Summary) {
println!("Breaking news! {}", item.summarize());
}
The documentation mentions that the above signature is just syntactic sugar for the below signature. Wouldn't it make sense to use trait Summary instead of impl Summary as impl can also be used to define methods on structs?
pub fn notify<T: Summary>(item: T) {
println!("Breaking news! {}", item.summarize());
}
Is there any hidden concept around it that I'm missing?
Contrary to languages such as Go or Java, Rust allows for both static and dynamic dispatch, and some syntax was required to let programmers choose between the two.
As dynamic dispatch must work on objects which might not be Sized, you need a reference to use it. That is, you would use &dyn Trait or Box<dyn Trait> (note: for historical reasons, the dyn keyword is not required, but modern Rust uses it). In C++, dynamic dispatch also requires a reference or pointer.
Static dispatch is not something Go or Java have. In C++, it works with templates and duck-typing. In Rust, it works with generics and traits, and its original syntax was:
fn some_function<T: Trait>(foo: T) { … }
Later, the following syntax was added to the language:
fn some_function(foo: impl Trait) { … }
which is equivalent to the above.
This syntax was originally invented to be used in return types, where there is no generic equivalent:
fn some_function() -> impl Trait { … }
This means that some_function can return any single type that implements Trait, but this type must be known at compile time. This has some performance benefits over returning Box<Trait> for example. In C++, the closest equivalent would be returning auto or decltype(auto).
The syntax in parameter position was added for symmetry.
You might wonder why not simply make the generics implicit and have:
fn some_function(foo: Trait) { … }
But this would be slightly confusing. Trait by itself is not sized, and therefore cannot be used as a parameter, unless they are generic. This would make traits stand out in the realm of unsized types. For example, if (foo: Trait) would work, you might wonder why (foo: str) doesn't, but what would that one mean? There is also other problems with making generics implicit, for example, generics in traits make the trait non-object-safe.
Later, Rust will likely extend those existential types and allow this at a module level:
type Foo = impl Bar;
(which is currently allowed on nightly, guarded by the type_alias_impl_trait feature)
Finally, you are asking why the syntax is impl Foo, rather than trait Foo. This reads well as "a type that implements Foo". The original RFC doesn't discuss alternative syntaxes much. Another RFC discusses the syntax more, in particular whether the syntax should have been any Foo in parameter position, and some Foo in return position. The syntax trait Foo was never considered as far as I am aware.
Let's say in a 3rd party library we have an interface and a struct implementing this interface. Let's also assume there is a function that takes ParentInterface as argument, which have different behavior for different types.
type ParentInterface interface {
SomeMethod()
}
type ParentStruct struct {
...
}
func SomeFunction(p ParentInterface) {
switch x := p.Type {
case ParentStruct:
return 1
}
return 0
}
In our code we want to use this interface, but with our augmented behavior, so we embed it in our own struct. The compiler actually allows us to call functions about ParentInterface on my struct directly:
type MyStruct struct {
ParentInterface
}
parentStruct := ParentStruct{...}
myStruct := MyStruct{parentStruct}
parentStruct.SomeMethod() // Compiler OK.
myStruct.SomeMethod() // Compiler OK. Result is same. Great.
SomeFunction(parentStruct) // Compiler OK. Result is 1.
SomeFunction(myStruct.ParentInterface) // Compiler OK. Result is 1.
SomeFunction(myStruct) // Compiler OK. Result is 0. (!)
Isn't the last case a problem? I've encountered this kind of bugs more than once. Because I'm happily use MyStruct as an alias of ParentInterface in my code (which is why I define it in the first place), it's so hard to always remember that we cannot call SomeFunction on MyStruct directly (the compiler says we can!).
So what's the best practice to avoid this kind of mistake? Or it's actually a flaw of the compiler, which is supposed to forbid the use of SomeFunction(myStruct) at all since the result is untrustable anyway?
There is no compiler mistake here and your experienced result is the expected one.
Your SomeFunction() function explicitly states it wants to do different things based on the dynamic type of the passed interface value, and that is exactly what happens.
We introduce interfaces in the first place so we don't have to care about the dynamic type that implements it. The interface gives us guarantees about existing methods, and those are the only things you should rely on, you should only call those methods and not do some type-switch or assertion kung-fu.
Of course this is the ideal world, but you should stick to it as much as possible.
Even if in some cases you can't fit everything into the interface, you can again type assert another interface and not a concrete type out of it if you need additional functionality.
A typical example of this is writing an http.Handler where you get the response writer as an interface: http.ResponseWriter. It's quite minimalistic, but the actual type passed can do a lot more. To access that "more", you may use additional type assertions to obtain that extra interface, such as http.Pusher or http.Flusher.
In Go, there is no inheritance and polymorphism. Go favors composition. When you embed a type into another type (struct), the method set of the embedded type will be part of the embedder type. This means any interfaces the embedded type implemented, the embedder will also implement those. And calling methods of those implemented interfaces will "forward" the call to the embedded type, that is, the receiver of those method calls will be the embedded value. This is unless you "override" those methods by providing your own implementation with the receiver type being the embedder type. But even in this case virtual routing will not happen. Meaning if the embedded type has methods A() and B(), and implementation of A() calls B(), if you provide your own B() on the embedder, calling A() (which is of the embedded type) will not call your B() but that of the embedded type.
This is not something to avoid (you can't avoid it), this is something to know about (something to live with). If you know how this works, you just have to take this into consideration and count with it.
Because I'm happily use MyStruct as an alias of ParentInterface in my code (which is why I define it in the first place)
You shouldn't use embedding to create aliases, that is a misuse of embedding. Embedding a type in your own will not be an alias. Implementations of existing methods that check concrete types will "fail" as you experienced (meaning they will not find a match to their expected concrete type).
Unless you want to "override" some methods or implement certain interfaces this way, you shouldn't use embedding. Just use the original type. Simplest, cleanest. If you need aliases, Go 1.9 introduced the type alias feature whose syntax is:
type NewType = ExistingType
After the above declaration NewType will be identical to ExistingType, they will be completely interchangeable (and thus have identical method sets). But know that this does not add any new "real" feature to the language, anything that is possible with type aliases is doable without them. It is mainly to support easier, gradual code refactoring.
I can't find a "clone" method convention in Golang, but it seems necessary to have one.
I only saw the built-in way *clonedObj = *obj, but it is too low-level, and can't handle (when-necessary) deep copy of case like struct { member *CompositionObj }
I doubt whether "func (obj ClassA) Clone() interface{}" prototype will work, because calling obj2 := obj.Clone() will "loose" the method set for ClassA, and need explicit code like obj2.(*ClassA) afterwards.
Please advice a working direction.
This answer to a similar question regarding maps suggests to use the gob package. The documentation states:
A stream of gobs is self-describing. Each data item in the stream is preceded by a specification of its type, expressed in terms of a small set of predefined types. Pointers are not transmitted, but the things they point to are transmitted; that is, the values are flattened. Nil pointers are not permitted, as they have no value. Recursive types work fine, but recursive values (data with cycles) are problematic. This may change.
so it may not be suitable for your use case.
That said, your question largely depends on your actual use-case. You do not need a generic way to deep-copy things usually, you can usually either get away with the built-in copy mechanics or write concrete copy functions for the types that actually need it.
An alternative might be the deepcopy package but I have no experience with it myself, I just found it on Goolge.
Ok, having some while no one else give me proper reference, I have found out some reference example how to clone in Go myself and want to share.
(Only upvote me a few if this answer is useful to you. I'm not for earning votes. Welcome other better answers and comments)
I found this protoype in package "github.com/jinzhu/gorm" (Database's ORM library) for reference:
func (s *DB) clone() *DB {
db := &DB{
...
}
...
return db
}
And similar pattern in package "golang.org/x/net/html/atom":
func (n *Node) clone() *Node {
m := &Node{
Type: n.Type,
...
}
...
return m
}
The above prototype is enough if the Clone()'s caller always know your object type when cloning. (and you need uppercase Clone() to make the method to be "public")
However, if you want advanced feature that a variable may hold any object of similar base interface, here is my sample:
func (t *T) Clone() YourBaseInterface
Where YourBaseInterface is:
type YourBaseInterface interface {
Clone() YourBaseInterface
OtherMethod1()
...
}
Or can merely use interface{} instead of YourBaseInterface in the return, and do a typecast like obj2 := obj.Clone().(*YourBaseType) after clone.
CAUTION
There is one drawback with this prototype. Becase Golang doesn't support this prototype as build-in, the Clone() method won't be called in some language's feature, e.g. when you copy(dest, src) a []YourTypeWithClone slice. Instead, it still do plain *elem2 = *elem1 struct copying. Solutions maybe either don't use those build-in, or you may flaw back to design the class struct members so that doing plain copy is enough for its copy purpose if possible.
I'm struggling with my learning of Go.
I found this neat implementation of a Set in go: gopkg.in/fatih/set.v0, but I'd prefer naming my sets with a more explicit name that set.Set, doing something like:
type View set.Set
In essence, I want my View type to inherit set.Set's methods. Because, well, View is a set.Set of descriptors. But I know Go is pretty peaky on inheritance, and typing in general.
For now I've been trying the following kinda inheritance, but it's causing loads of errors when trying to use some functions like func Union(set1, set2 Interface, sets ...Interface) Interface or func (s *Set) Merge(t Interface):
type View struct {
set.Set
}
I'd like to know if there's a way to achieve what I want in a Go-like way, or if I'm just trying to apply my good-ol' OO practices to a language that discards them, please.
If anyone else is coming back to this question, as of Go 1.9 type aliases are now supported.
A type alias has the form: type T1 = T2
So in your example you can just do type View = set.Set and everything will work as you want.
Note, I think the simple aliasing you proposed initially is syntactically valid though having had a quick look at the set library, rather than aliasing set.Set it might make more sense to alias set.Interface, e.g.:
package main
import (
"fmt"
set "gopkg.in/fatih/set.v0"
)
// View is a type alias for the set.Interface interface
type View set.Interface
// Display takes one of our View types in order to print it.
func Display(view View) {
fmt.Println(view.List())
}
func main() {
// create our first set.Interface or View
v1 := set.New()
v1.Add("foo")
// create our second set.Interface or View
v2 := set.New("bar")
// call a set function
v3 := set.Union(v1, v2)
// call our function that takes a View
Display(v3)
}
You may have noticed I'm cheating somehow because I make no real mention of the aliased type in the above code other than in defining the parameter to the Display function above which you'll note takes in a View instance rather than a set.Interface. If you have lots of functions working on these things, then that might read more expressively for your domain.
Note that because our View type is an alias to an interface type, it precludes adding your own functions to that type as Go doesn't allow us to have an interface receiver type for a function (I might be expressing that incorrectly). By this I mean that you can't do anything like:
func (v View) Display() string {
return v.String()
}
In summary I think aliasing things is fine, it can make internal APIs more readable, and you can lean on the compiler to help eliminate certain classes of errors; however this doesn't allow you to add functionality to the custom type. If this is required an alternate approach would be necessary, either embedding or simple composition (i.e. a View has a Set).
Suppose that I have a type type T intand I want to define a logic to operate on this type.
What abstraction should I use and When ?
Defining a method on that type:
func (T t) someLogic() {
// ...
}
Defining a function:
func somelogic(T t) {
// ...
}
Some situations where you tend to use methods:
Mutating the receiver: Things that modify fields of the objects are often methods. It's less surprising to your users that x.Foo will modify X than that Foo(x) will.
Side effects through the receiver: Things are often methods on a type if they have side effects on/through the object in subtler ways, like writing to a network connection that's part of the struct, or writing via pointers or slices or so on in the struct.
Accessing private fields: In theory, anything within the same package can see unexported fields of an object, but more commonly, just the object's constructor and methods do. Having other things look at unexported fields is sort of like having C++ friends.
Necessary to satisfy an interface: Only methods can be part of interfaces, so you may need to make something a method to just satisfy an interface. For example, Peter Bourgon's Go intro defines type openWeatherMap as an empty struct with a method, rather than a function, just to satisfy the same weatherProvider interface as other implementations that aren't empty structs.
Test stubbing: As a special case of the above, sometimes interfaces help stub out objects for testing, so your stub implementations might have to be methods even if they have no state.
Some where you tend to use functions:
Constructors: func NewFoo(...) (*Foo) is a function, not a method. Go has no notion of a constructor, so that's how it has to be.
Running on interfaces or basic types: You can't add methods on interfaces or basic types (unless you use type to make them a new type). So, strings.Split and reflect.DeepEqual must be functions. Also, io.Copy has to be a function because it can't just define a method on Reader or Writer. Note that these don't declare a new type (e.g., strings.MyString) to get around the inability to do methods on basic types.
Moving functionality out of oversized types or packages: Sometimes a single type (think User or Page in some Web apps) accumulates a lot of functionality, and that hurts readability or organization or even causes structural problems (like if it becomes harder to avoid cyclic imports). Making a non-method out of a method that isn't mutating the receiver, accessing unexported fields, etc. might be a refactoring step towards moving its code "up" to a higher layer of the app or "over" to another type/package, or the standalone function is just the most natural long-term place for it. (Hat tip Steve Francia for including an example of this from hugo in a talk about his Go mistakes.)
Convenience "just use the defaults" functions: If your users might want a quick way to use "default" object values without explicitly creating an object, you can expose functions that do that, often with the same name as an object method. For instance, http.ListenAndServe() is a package-level function that makes a trivial http.Server and calls ListenAndServe on it.
Functions for passing behavior around: Sometimes you don't need to define a type and interface just to pass functionality around and a bare function is sufficient, as in http.HandleFunc() or template.Funcs() or for registering go vet checks and so on. Don't force it.
Functions if object-orientation would be forced: Say your main() or init() are cleaner if they call out to some helpers, or you have private functions that don't look at any object fields and never will. Again, don't feel like you have to force OO (à la type Application struct{...}) if, in your situation, you don't gain anything by it.
When in doubt, if something is part of your exported API and there's a natural choice of what type to attach it to, make it a method. However, don't warp your design (pulling concerns into your type or package that could be separate) just so something can be a method. Writers don't WriteJSON; it'd be hard to implement one if they did. Instead you have JSON functionality added to Writers via a function elsewhere, json.NewEncoder(w io.Writer).
If you're still unsure, first write so that the documentation reads clearly, then so that code reads naturally (o.Verb() or o.Attrib()), then go with what feels right without sweating over it too much, because often you can rearrange it later.
Use the method if you are manipulating internal secrets of your object
(T *t) func someLogic() {
t.mu.Lock()
...
}
Use the function if you are using the public interface of the object
func somelogic(T *t) {
t.DoThis()
t.DoThat()
}
if you want to change T object, use
func (t *T) someLogic() {
// ...
}
if you donn't change T object and would like a origined-object way , use
func (t T) someLogic() {
// ...
}
but remeber that this will generate a temporay object T to call someLogic
if your like the way c language does, use
func somelogic(t T) {
t.DoThis()
t.DoThat()
}
or
func somelogic(t T) {
t.DoThis()
t.DoThat()
}
one more thing , the type is behide the var in golang.