The transferFrom method in both ERC20 and ERC721 is used to transfer tokens from one account to another. However, as you noted, the semantics of the method in each standard are different.
In ERC20, the uint256 argument is the amount of tokens to transfer, which makes sense because all tokens are identical.
In ERC721, the uint256 argument is the unique identifier for the token to transfer, which makes sense because each token is unique.
The safeTransferFrom method in ERC721 was introduced to add an additional layer of safety. When you transfer a token to a contract, the contract can implement the onERC721Received function to react to the transfer. If the contract does not return the correct magic value from onERC721Received, then safeTransferFrom will revert the transaction. This is to prevent tokens from being accidentally lost by being transferred to contracts that are not designed to handle them.
So why does transferFrom exist in ERC721? There could be several reasons:
Backwards compatibility: ERC721 was designed to be a superset of the ERC20 interface, which includes transferFrom.
Flexibility: transferFrom gives more flexibility to developers. They can decide whether they want to use transferFrom or safeTransferFrom based on their specific use case.
Gas costs: transferFrom can be cheaper in terms of gas because it doesn't require a call to onERC721Received.
safeTransferFromat all. There are other mechanisms for providing the necessary safety. And tbh there was not enough consensus in 2018 to coalesce around that. So now we have both and you are free to use either or both. – William Entriken Apr 17 '21 at 18:16