I guess I'll say that minicomputer and mainframe implementations of languages like Pascal and FORTRAN and COBOL and even BASIC back in the 1970s were frequently built with interoperability in mind, like you bought all the compilers in one big bundle. (Maybe like gcc or the the llvm-based compiler suite today?) It might have been easier because most of these languages had minimal runtime systems and the science of activation records and language implementation was mostly figured out by 1975 when Scheme came out with closures.
More modern languages have data structures that are similar but different that are a challenge. For instance the list types in Java and Python as they are used in common software are similar in a lot of ways but different. C has arrays in the stdlib but not that kind of expandable list, C developers might write their own or get one out of a library, I guess C++ has std::list but that doesn't have a fast way to get the n-th element of a list. Common Lisp has its own idea of a "list" and Clojure lives in the JVM and can access a Java List just fine but has its own immutable list which has a totally different API than thoses other language (e.g. no nconc!)
In general it is not so hard to access those objects through the 'foreign function API' or whatever you have but you either write stuff in the host language which is built especially to work with the guest language, or you make a wrapper, or you copy the data structures wholesale. It is never going to be trivial.
Similarly a language like Java has garbage collection and Python and Rust have their own forms of reference counting, either way you will either do your own memory management in the guest language or use some APIs to participate in the memory management of the host but it is a hassle.
For a long time the standard architecture for complex systems has been a scripting language + a systems language. Like Lua and C or Python and C or maybe Clojure and Java.
So imagine whatever Rust has with C but with many other languages.