First of all, forgett about multi inheritance, this most cerntainly only complicates it (I don't know why people think that this is a good explaination, inheritance in itself is much more complex to expalin than interfaces).
To make it simple, at it's core, an interface is very simple, it is a promise that the class you are writing contains a certain set of functions (and properties). Then using this interface in your code is equivalent to saying: "I don't care what kind of type something is as long as it holds to the promise made in the interface".
For example:
type
IPrintable = interface
function ToString: String;
end;
Any class that uses this interface promises that there will be a "ToString" function. So for example, if you write a logger, which shall log whenever some class get's created, the logger does not care what class that is, as long as it can write it to the log file. Therefore the logger would have something like this:
procedure TLogger.LogClassCreation(CreatedClass: IPrintable);
begin
LogLine('New Class instance Created: ' + CreatedClass.ToString);
end;
As you can see the LogClassCreation does only need the ToString function and nothing else, therefore it only requires a promise that the class will have this method, and this promise is made by having the type of the parameter being the Interface that creates this promise.
In this respect Interfaces are the same across most languages, and it is also like in golang.
Now comes the complicated part. Pascal has two types of interfaces, CORBA and COM. CORBA interfaces are the "raw" interfaces, which are basically just the promise I described above. To use them simply add a {$Interfaces CORBA} to your unit and you have the simple, raw, go-like interfaces.
The other (default) form of interfaces are COM interfaces, which basically add reference counting to it. If a class implements a COM interface it must also implement the _AddRef, _Release and and QueryInterace function. There you would need to implement your own reference counter. But, to make it simple, FPC provides the TInterfacedObject Class, which already implements those functions, so if you inherit from it, you don't have to care about it.
The "problem" with COM interfaces is, as they are reference counted, but normal pascal classes are manually managed, you cannot combine those two paradigms as easiely. So if you have a COM interface, it's best to only touch it via the interface. So for example, while this code is completely fine with CORBA interfaces:
program Project1;
{$mode objfpc}{$H+}
{$Interfaces CORBA}
type
IPrintable = interface
function ToString: String;
end;
TTest = class(TInterfacedObject, IPrintable)
public
function ToString: ansistring; override;
end;
procedure LogClassCreation(CreatedClass: IPrintable);
begin
WriteLn('New Class instance Created: ' + CreatedClass.ToString);
end;
function TTest.ToString: ansistring;
begin
Result := 'TTest';
end;
var
t: TTest;
begin
t := TTest.Create;
try
LogClassCreation(t);
finally
t.Free;
end;
end.
When interfaces change the [$Interfaces CORBA} to COM (or delete it as COM is the default), it will cause a double free, because when entering "LogClassCreation", the reference counter is increased to 1, and when leaving it dropps to 0 and is therefore freed, even though the main code still has the reference "t" but because this is not a COM interface reference, it does not count into the reference counter.
Therefore when you use something as a COM interface once, it's best to use it always as a COM interface.
So while COM and CORBA in principle serve the same purpose, to give promises about functionality, generally CORBA interfaces are much simpler and are more like your Go interfaces, while COM provide powerful reference counting, but on the other hand are not as easiely compatible with "normal" pascal code.
This also means that if you want to use many small interfaces (for encapsulation), COM is usually not that useful for that, because you must always be handling at least one COM interface, so if you only have a lot small ones, you need to do a lot of conversions (because remember, you always must touch the object as an interface), while with CORBA, you handle your object like a normal Pascal object for the most parts, and only use it as the interface when needed.
For me the rule of thumb is: I always use CORBA when I want to use interfaces as the classical "promise", and COM only if I need a reference counted pointer (smartpointer) for my class, and in those cases my COM interface is basically just a complete copy of the "public" parts of the class I am writing, and I just use this rather than the class itself.
An example where the latter might come in useful is for example the GMP Unit:
var a, b: MPInteger;
begin
a := 42;
b := 3;
a := a * b + b;
end;
Here MPInteger is a COM interface, and therefore, even though a and b are classes, because they are reference counted, there is no need of writing ".Free" anywhere and you can use operators. But you need to always use a and b as MPInteger and never as the underlying class, because otherwise you would run into memory management issues