It can't be used as a literal build dependency [0], no. However, the fact that your crates uses SNAFU should [1] be completely hidden from your users. From the outside, you just return a regular enum or struct as your error type. If you were to look at the symbols in the resulting binary, I would expect that you could see references to the trait method `snafu::ResultExt::context` (and similar functions across similar types) depending on how well the code was inlined. If you use other features like `snafu::Location` or `snafu::Report`, those would definitely show up.
You don't have to use the macros, no. When you define your error type, you can mark a field as `#[snafu(implicit)]` [2]. When the error is generated, that field will be implicitly generated via a trait method. The two types this is available for are backtraces and locations, but you could create your own implementations such as grabbing the current timestamp or a HTTP request ID.
[1]: There's one tiny leak I'm aware of, which is that your error type will implement the `snafu::ErrorCompat` trait, which is just a light polyfill for some features not present on the standard library's `Error` trait. It's a slow-burn goal to remove this at some point, likely when the error "provider API" stabilizes.
* can it be used as a build dependency (i.e symbols from the snafu crate don't appear in the generated code).
* I assume you have to use one of the macros (ensure! or location!) when constructing an error that contains a location?