Instead of implementing `consume()` for each integer type separately, you can implement a *single generic* method: mutating func consume<T: FixedWidthInteger>() throws -> T { let size = MemoryLayout<T>.size guard idx + size <= bytes.count else { throw Error.notEnoughBytes } defer { idx += size } return bytes[idx..<idx + size].enumerated().reduce(0) { $0 + T($1.element) << (8 * $1.offset) } } which can be used for all signed and unsigned integer types `(U)Int`, `(U)Int8`, ..., `(U)Int64`. Another option is to copy the bytes into a value of the desired type instead of bit shifting and adding: mutating func consume<T: FixedWidthInteger>() throws -> T { let size = MemoryLayout<T>.size guard idx + size <= bytes.count else { throw Error.notEnoughBytes } var value: T = 0 bytes.withUnsafeBytes { _ = memcpy(&value, $0.baseAddress! + idx, size) } idx += size return T(littleEndian: value) } ---------- Instead of letting the compiler infer the return type from the context, one can alternatively pass it as a parameter: mutating func consume<T: FixedWidthInteger>(_: T.Type) throws -> T { ... } which is then – for example – called as let crc32 = try buffer.consume(UInt32.self) ---------- I would probably call the method `get()` or `read()` instead of `consume()`. ---------- Defining a local `enum Error` type which conforms to the (global) `Error` protocol is possible, but might be confusing to the reader. I would use a different name for the concrete error type, for example: enum ReadError: Error { case notEnoughBytes } ---------- Now let's have a look how an error would be reported. The caller does not know the actual error type, so a typical calling sequence is: do { let someData = Data(bytes: [1]) var buffer = ConsumableByteArray(data: someData) let crc32: UInt32 = try buffer.consume() print(crc32) } catch { print(error.localizedDescription) } This produces the output: <pre> The operation couldn’t be completed. (MyProg.ConsumableByteArray.ReadError error 0.) </pre> This can be improved by adopting the `LocalizedError` protocol (see for example [How to provide a localized description with an Error type in Swift?](https://stackoverflow.com/q/39176196/1187415) on Stack Overflow): enum ReadError: Error, LocalizedError { case notEnoughBytes public var errorDescription: String? { switch self { case .notEnoughBytes: return "Not enough bytes in buffer" } } } Now the error output of the above program becomes <pre> Not enough bytes in buffer </pre> You can even store additional information about the error in associated values: enum ReadError: Error, LocalizedError { case notEnoughBytes(available: Int, needed: Int) public var errorDescription: String? { switch self { case .notEnoughBytes(let available, let needed): return "Not enough bytes in buffer (available: \(available), needed: \(needed))" } } } Then by throwing throw ReadError.notEnoughBytes(available: bytes.count - idx, needed: size) an error message like <pre> Not enough bytes in buffer (available: 1, needed: 4) </pre> is produced. ---------- Finally note that there is a [`ByteBuffer`](https://apple.github.io/swift-nio/docs/current/NIO/Structs/ByteBuffer.html) type as part of the [SwiftNIO](https://github.com/apple/swift-nio) framework, which can do all this and more. Even if you decide not to use it, having a look at its documentation and interface might be instructive.