How does the loopback interface work

The loopback interface is a virtual interface. The only purpose of the loopback interface is to return the packets sent to it, i.e. whatever you send to it is received on the interface. It makes little sense to put a default route on the loopback interface, because the only place it can send packets to is the imaginary piece of wire that is looped from the output of the interface to the input. There is nothing that can change this behaviour of the loopback interface, that's what it is coded to do.

When you ping 10.0.3.2, the reply does not come from some external device, but from the loopback interface itself. When you add an address on the loopback interface with e.g.

sudo ip addr add 10.0.3.1/24 dev lo

a route to 10.0.3.0/24 is added. You can see this with

ip route show table local

Something like

local 10.0.3.0/24 dev lo proto kernel scope host src 10.0.3.1

should show up. This routing table entry tells that a packet sent to any address between 10.0.3.1 and 10.0.3.254 is sent via the lo interface, from which it is immediately returned.

EDIT: clarification as a response to the comment below.

Here is what happens when you ping 10.0.3.2: the kernel gets an IP packet for delivery with a destination address 10.0.3.2. Just like with any packet to be delivered, the kernel consults the routing table. In this case the matching entry is this: local 10.0.3.0/24 dev lo proto kernel scope host src 10.0.3.1, which says the packet should be delivered via the lo interface with the source address 10.0.3.1.

Now, because the packet was given to the lo interface, the loopback interface does what it normally does: it takes the packet off the send queue and puts it on the receive queue. From the kernel's point of view, we have now received an incoming packet ready for consumption by a server process listening on a socket. (In the case of ping, the kernel processes it internally.) We have now received a "remote" ICMP packet with a destination address of 10.0.3.2, which is arguably not one of our local addresses, but it was delivered to the loopback interface nonetheless.

Next, the kernel sends a response to the ping: an ICMP response packet with the addresses reversed: 10.0.3.2 as source address and 10.0.3.1 as destination. This is delivered via the loopback interface back to the ping program, which shows that we got a reply from 10.0.3.2.