When you're serializing to a string, I would err towards just returning the string. Manipulating the result I wouldn't consider a pro. Being able to support more usecases more easily seems like a bigger win.
The bigger benefit for the sink is when you're using non-string representations. Then you can stream and otherwise manipulate the sink to better ...
Both aren't "restricting" the user of such API.
In the first case, I can provide a buffer ("sink") to copy the serialized output to, and use it as in the second case.
In the second case, I can just provide the returning result to the sink.
This is from a strictly usability perspective.
You suggest in your question that there is a "leaky abstraction" ...