c++swiftstringinterop

How to return a String from swift to cpp caller


I am working on a C++ library with a Swift backend (the C++ code mainly serves as a bridge to Rust).

I have the following:

char* get_string(void* pBackend) {
    Backend* backend = (Backend*) pBackend;
    return backend.get_string ???; // or does this need more processing?
}

In turn Rust will call get_string from the cpp library, but never mutate it.

The question is how to properly handle this from the Swift side, I have lost count of all the numerous pointer swift and cpp types.

in Swift:

struct Backend {
  var myString: String? = nil
  func getString() -> ??? { // which type to return?
    if let s = myString {
      return ??? // how to get it?
    } else {
      "no String"
    }
  }
}

I know there may be some lifetime concerns, but as soon as Rust call get_string, it copies the contents.

(The backend has more internal functions that will actually set myString.)


Solution

  • It is possible to just return a Swift String on the Swift side. Swift Strings can be easily converted to std::strings. See also the documentation.

    // Make these public so that they can be accessed from C++
    public struct Backend {
        var myString: String? = nil
        
        public func getString() -> String {
            return myString ?? "no String"
        }
    }
    

    On the C++ side, you can do:

    char* get_string(void* pBackend) {
        Backend* backend = (Backend*) pBackend;
        swift::String stringFromSwift = backend->getString();
        std::string cppString = (std::string)stringFromSwift;
        return strdup(cppString.c_str());
    }
    

    Since the string needs to outlive get_string, it needs to be copied to the heap. The caller of get_string is responsible for freeing this.