Let me elaborate a bit on the valid answer from @pbsh.
As mentioned, they behave quite differently and are for different use case. Events can't be monitored by other smart contracts, only by external systems monitoring the blockchain (and the specific contract). Partially the opposite is true for return values.
What I mean by 'partially' is that an external system can't directly access the (possible) return values of a transaction. The typical solution is to emit events with required data and catch those when the transaction is processed. However, if a static call (not a transaction) is used, you have access to the return values.
As for gas considerations, return values are actually cheaper to use than events. In the following test contract executing testReturn
costs 21401 gas and executing testEmit
costs 22506 gas.
Example code:
pragma solidity ^0.8.0;
contract GasTest {
event A(uint256);
function testReturn() public returns (uint256) {
return 5;
}
function testEmit() public {
emit A(5);
}
}