Accepting interfaces is an implicit commitment to support external implementations of the interface. You can't add a method to an interface without breaking clients who do this. This makes interfaces more fragile than structs.
A concrete example: If I have a NewFoo function that returns a Foo and a FooTaker function that accepts a FooInterface, I can't assume that new features I add in NewFoo are usable from FooTaker. If FooTaker just took a Foo, there would be no problems.
This is, I think, the best rule of thumb for when to use an interface: If you expect consumers of your library to write their own data types that could be used by your library, define an interface they can satisfy. Otherwise just produce and consume a struct you control -- it will be much more flexible and maintainable in the long run.
If "Foo" represents anything at all complex or stateful (e.g. a database connection, a model of some data, an HTTP server, etc.), and you might want to extend it later, it's going to be messy.
If you assume there is only ever going to be one implementation of "Foo", then there's nothing to be gained by using an interface over a struct with private fields. If there are other implementations, then you have to update every implementation in sync with the interface every time you add or change a method. That might be very difficult or even nigh impossible if they're in different packages maintained by different people.
My point is to be very careful with how you use interfaces. They're designed to represent abstractions that apply to multiple data types. Looking at the standard library, Reader, Writer, Stringer, http.Handler etc are all pretty useful as interfaces. database/sql/driver has the Driver interface, which is about as complicated as you should allow an interface to become. Notice that nothing in database/sql implements the Driver interface -- it's strictly for polymorphism, to consume external implementations -- and nearly every type in database/sql is a raw struct.
A concrete example: If I have a NewFoo function that returns a Foo and a FooTaker function that accepts a FooInterface, I can't assume that new features I add in NewFoo are usable from FooTaker. If FooTaker just took a Foo, there would be no problems.
This is, I think, the best rule of thumb for when to use an interface: If you expect consumers of your library to write their own data types that could be used by your library, define an interface they can satisfy. Otherwise just produce and consume a struct you control -- it will be much more flexible and maintainable in the long run.