Hi Lorenzo,
After diving into the engine source code for a bit, I came to the conclusion that this behavior is indeed by design. Unreal treats USTRUCTs as value types for the most part, so passing them by reference should be considered more of a convenience rather than an optimization. The copy comes from the following code, which runs on the beginning of a UFUNCTION called from blueprints, for each struct passed by reference:
#define PARAM_PASSED_BY_REF(ParamName, PropertyType, ParamType) \ ParamType ParamName##Temp; \ ParamType& ParamName = Stack.StepCompiledInRef<PropertyType, ParamType>(&ParamName##Temp);
The workaround here would be to either use a UObject instead of a struct, or to wrap your large struct inside a UObject. In recent UE versions, you can also try to use FInstancedStruct, although that offers much more than you probably need.
Having said all that, there is one thing that got me curious. The code I showed above creates a temporary structure, which makes it look like that would be the actual target of the reference passed into the native function (later requiring a copy back into the actual structure). However, this is not what happens, at least in recent UE versions. I added some code to your repro that prints the addresses of the structures being used, and I could see that, in the end, the actual struct, not the temporary one, is passed into the native function. So, this temporary copy does not really seem necessary. It could be required by some deeper machinery of the blueprint VM that I am not aware, though. I’ll try to ask around and see if anyone from Epic can shed more light on this. But in any case, the recommendation for you at this time would be to use one of the approaches I mentioned above to work around this.
Best regards,
Vitor