Calling Go code from Dart using cgo and Dart FFI with a simple example

The key motivation for writing this article is the fact of a strong lack of information (especially in the Russian-speaking community) on the use of cgo and Dart FFI to call Go code from the Dart language.





The Dart language, despite its growing popularity, at the moment still does not have the same large community as the Go language. Dart is geared towards performing other tasks, so it sometimes does not contain the implementations and functionality that Go already has.





If you can avoid exporting go code to Dart (for example, exporting a ready-made library), then it is better to use this opportunity and not use cgo. However, there may be cases when distilling go into dart code is the optimal solution (for example, you are already familiar with Go and Dart, and do not want to write code in C, in which case it makes sense to think about using cgo and Dart FFI).





In this article, using a simple example, it will be shown how you can call Go code from the Dart language (for example, in Flutter applications).





What needs to be installed:





  • Go





  • Dart





  • Text editor / IDE (I will be using VSCode, as it is the most popular environment among the Dart and Go community, special plugins will also be installed to support Go and Flutter languages)





Step 1 - Create a Blank Dart Console Application

Command Palette F1 Dart, Console Application ( , cgo Flutter Dart).





, cgo_dartffi_helloworld, . ( Dart, ffi pubspec.yaml ).





.





2 - ffi yaml

ffi yaml go dart.





name: cgo_dartffi_helloworld
description: A sample command-line application.
version: 1.0.0

environment:
  sdk: '>=2.12.0 <3.0.0'

dependencies:
  path: ^1.8.0
  ffi: ^0.1.3

dev_dependencies:
  pedantic: ^1.10.0
  test: ^1.16.0
      
      



3 - .go

go, ( , lib.go) Dart. - HelloFromGo().





// filename: lib.go
package main

import "C"

//export HelloFromGo
func HelloFromGo() *C.char {
	message := "Hello to dart lang from go"
	return C.CString(message)
}

func main() {}

      
      



cgo , . cgo (, ), ( export). cgo https://golang.org/cmd/cgo/, .





4 - go

:





go build -buildmode=c-shared -o lib.a lib.go
      
      



lib.a ( c ). ( , go, go, cgo).





5 -

:





:





  • pubspec.yaml





  • lib.h, lib.a lib.go





  • bin dart ( )





6 - cgo Dart

( ) . ( ), .





  • 6.1 - bin/cgo_dartffi_helloworld.dart





  • 6.2 - ( ffi utf8 )





import 'dart:ffi' as ffi;
import 'package:ffi/src/utf8.dart';
      
      



  • 6.3 -






final dylib = ffi.DynamicLibrary.open('lib.a');

      
      



  • 6.4 - dart





typedef HelloFromGo = ffi.Pointer<Utf8> Function();
typedef HelloFromGoFunc = ffi.Pointer<Utf8> Function();
final HelloFromGo _finalFunction = dylib
    .lookup<ffi.NativeFunction<HelloFromGoFunc>>('HelloFromGo')
    .asFunction();
      
      



  • 6.5 - ( , .toDartString C Dart):





void main() {
  print(_finalFunction().toDartString());
}
      
      



go, string Dart.





Further, when writing your functions, you should take into account that the data formats in the Go, C and Dart languages ​​may differ (and often this happens), which leads to the need to use various conversions on the go / dart code side, for more details, see the following links:





Full Dart code:





import 'dart:ffi' as ffi;
import 'package:ffi/src/utf8.dart';

final dylib = ffi.DynamicLibrary.open('lib.a');

typedef HelloFromGo = ffi.Pointer<Utf8> Function();
typedef HelloFromGoFunc = ffi.Pointer<Utf8> Function();
final HelloFromGo _finalFunction = dylib
    .lookup<ffi.NativeFunction<HelloFromGoFunc>>('HelloFromGo')
    .asFunction();

void main() {
  print(_finalFunction().toDartString());
}

      
      



If you need to pass parameters to the called function, you can use pointers and declare them in the called function, for example:





typedef GetHash = Pointer<Utf8> Function(Pointer<Utf8> str);
typedef GetHashFunc = Pointer<Utf8> Function(Pointer<Utf8> str);
final GetHash _getHashGoFunction =
    _lib.lookup<NativeFunction<GetHashFunc>>('GetHash').asFunction();
      
      



The main thing to remember is that you need to check the formats of the transmitted data.








All Articles