It's not complicated. Your return value can, as in Go, be an error or an object of type T; unlike Go and its stunted type system, this is a generic object and so this must be written once for all types. That return value can be functionally mapped over, just like `map` in Ruby, and will return either the original exception (the mapping function will not be called) or the result of the mapping function.
These can be chained indefinitely. At the end you have a Try object of the generic type of the final mapping function, against which you can pattern match for Success or Failure. (Failing to compare against both Success and Failure is a compiler warning. There's a flag, IIRC, to make it an error, but since my IDE is already yelling at me that I screwed up I don't in practice have this problem.)
Additionally, at any point in the chain you can call `recover`, which is similar to `map` but for the exceptional case (Try[T] -> Try[T], with Success[T] passed through) if it's a recoverable error.
All error handling--as separate from error recovery, which is rarely in practice useful--is always, always, always at the very bottom of the function, right next to the happy path. It literally is impossible to miss, and does not require copy-pasting code after every library function that can fail.
There's no reason to check 'err' every time unless your language is just straight-up too dumb to do work for you. Your time is more valuable than the computer's in all but the most extreme of cases; you should act like it.