Search code examples
pythonc++cpython

Python Cpp API And Immutable Strings


I'm currently working on a project using this open source library (nanobind) and am trying to understand how python enforces immutability. Lets say I have some memory allocation in C with the following:

std::string& func(){
  auto ptr = malloc(10);
  auto str = new (ptr) string("abcdefghij", 10);
  return *str;
}

using nanobind, the above returns a python string by reference to the allocation. Now lets say that I have a way to get access to ptr (the address of the str returned to python) in another function and do something like

memcpy(ptr, "bbb", 3); 

If I'm updating the memory address where the python string is stored, why doesn't the string itself change in python? I know it's immutable, but not sure how that's really enforced here if I'm not copying/returning by reference.

Here is some detail on nanobind's return by reference policy:

"Create a thin Python object wrapper around the returned C++ instance without making a copy, but do not transfer ownership to Python. nanobind will never call the C++ delete operator, even when the wrapper expires. The C++ side is responsible for destructing the C++ instance."


Solution

  • The answer to your question is linked in the documentation you showed:

    The reason is that type casters convert function arguments and return values once, but further changes will not automatically propagate to across the language barrier because the representations are not intrinsically linked to each other.

    You can read the (very simple) conversion code for std::string and see for yourself:

        static handle from_cpp(const std::string &value, rv_policy,
                               cleanup_list *) noexcept {
            return PyUnicode_FromStringAndSize(value.c_str(), value.size());
        }
    

    where PyUnicode_FromStringAndSize is documented as:

    The buffer is copied into the new object.

    So any changes you make to the source string are not visible in python because python has its own private copy.