-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Doctests don't work in bin targets, non-public items #50784
Comments
It sounds weird to have documentation examples in a bin target, don't you think? There is no documentation to generate so why would there be documentation's code to run? |
This bug is based on feedback from a user. Cargo makes new users create bin targets by default, so the first time a new Rust user tries doc tests, it's probably in a bin. There's a mismatch between what rustdoc does (tests publicly-visible documentation) and expectations (that tests in doccomments, in general, are run). |
Maybe we give bad information ahead? I wonder if users know the difference between comments and doc comments... For now I don't see this as a rustdoc bug but as a misunderstanding that should be resolved in tutorials/books. cc @rust-lang/docs |
Maybe doctests should just run unconditionally? The code for a non-public bin-crate item may end up in a public, lib-crate item later, so what's wrong having documentation and tests a bit earlier? |
The problem with running doctests on bin targets lies with how doctests work. The way rustdoc creates doctests is by building the code sample as a bin target of its own, and linking it against your crate. If "your crate" is really an executable, there's nothing to link against. If you run
Semantically, i find nothing wrong with allowing doctests in bin crates. However, to pull this off, we would need to tell Cargo to compile bin crates as libs, then hand that rlib to rustdoc for testing. There's nothing stopping us from doing that right now:
cc @rust-lang/cargo |
Wow, that's a nice surprise that it works. I was afraid that duplicate |
Yeah, the main in your bin goes inside the lib crate, so it's just a regular function at that point. I did have to tag it with |
I just hit this today. I came here after finding rust-lang/cargo#2009 in a Google search, which mentions that it is 'addressed in the documentation'. I suspect it may have been at one point but it doesn't appear to be now. I checked https://doc.rust-lang.org/book/ch11-01-writing-tests.html and from there the link https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests (Of course it's possible its mentioned somewhere and I missed it. A very careful reading of the surrounding text implies it, because it's all talking about libs, but nowhere I see is it mentioned explicitly.) |
I ran into this today, and found it confusing. I would love to be able to write doctests in the modules of my binary crate. What would it take to make that work out-of-the-box? |
I can think of a few hurdles that someone would need to address:
/// ```
/// do_something();
/// ```
fn do_something() {}
fn main() {} Here,
One (rough) idea to address this would be to fundamentally change how fn do_something() {}
#[test]
fn do_something_doctest() {
do_something();
} So all local (private) symbols would be in scope. That's a pretty radical difference, so I'm not sure if it would fly (would probably be confusing if there are two styles of doctests). Also, I think it's worth examining if this is a good idea from the start. Documenting binaries still seems like a strange concept to me. Why can't you just move all the code to a library? |
On Sat, Mar 28, 2020 at 02:23:19PM -0700, Eric Huss wrote:
I can think of a few hurdles that someone would need to address:
* How to resolve symbols in a binary? Doctests behave as-if they are a separate crate linking in the library. But a binary is normally not used a library, so it has no name to refer to. For example:
```rust
/// ```
/// do_something();
/// ```
fn do_something() {}
fn main() {}
```
Here, `do_something();` would fail because it is not in scope. You could try to scope it with the name of the binary (`mything::do_something();`), but then you have two new problems: what if you have a lib target of the same name? And, deal with the fact that you normally don't export any public items in a binary.
* How to deal with lack of public exports? Building a binary as a library will result in a lot of unused warnings. And since rustdoc imports the crate, usually nothing will be publicly exported, so you won't be able to access anything.
One (rough) idea to address this would be to fundamentally change how `rustdoc --test` works. Instead of creating a synthetic test that links to a library, embed the tests directly in the module. So it would rewrite the above example to:
```rust
fn do_something() {}
#[test]
fn do_something_doctest() {
do_something();
}
```
So all local (private) symbols would be in scope.
I would love to see that as the *normal* approach to doctests,
personally. It would also unify the different kinds of tests. I do agree
that that would be a more invasive change, though.
Also, I think it's worth examining if this is a good idea from the start. Documenting binaries still seems like a strange concept to me. Why can't you just move all the code to a library?
Why should the concept of documentation and testing be limited to
libraries, and not supported in binary crates? I use documentation in
binary crates to document the code for later understanding. I'd like to
use tests to verify individual modules of functionality; that doesn't
necessarily mean I want to split that functionality into a separate
library crate.
|
Just hit this today. I also frequently document internal modules of binary crates and intuitively expected that doctests would still work. Effectively, I use internal modules as non-public libraries. The reason I do not make them into separate library crates is that they are often not useful in a general context, but I do sometimes end up turning them into libraries. This kind of flexibility seems like a win. |
Hey, so I just learnt about this while learning about the language. It's disappointing, and I'm not sure this has been prioritised so it doesn't sound like it's going to be addressed in the near future. Anyways, I guess the only way then is to do the plebeian way and write a test for it |
You can split your crate into two parts: the binary part (containing only the |
@GuillaumeGomez Do you have a good example or minimum POC with like |
Sure, here you go: https://github.com/gtk-rs/gir/blob/master/Cargo.toml |
I think mostly for consistency. If someone is used to writing docs with rustdoc and doctests on their library functions, it makes sense that they would want apply the same documentation style to their bin functions. On a small project, having to separate out helper functions to a separate library is an extra hoop to jump through, especially if it is just for the sake of getting tests to run. I think underneath this is the assumption that people don't need to generate documentation for binaries. But in a team environment, its a good idea to document as much as you can, including examples. Having to switch documentation and test styles between libraries and binaries seems like an arbitrary constraint. |
Yes, I even document extensively for myself! And you can never be sure whether someone will join you on a project in the future. |
It's not quite this, exactly. Rustdoc is a tool for documenting a Rust API. A binary does not have a Rust API. So rustdoc is not really meant for documenting binaries. Where the friction really comes is when you want to produce internal documentation. This is a concern for libraries too, and that's why |
Doccomments unfortunately couple two things, but I think in this case the concern of running (doc)tests could be separate from concern of documenting things. Some tests like To look at it from a different perspective: tests are run by |
@joshtriplett Take a look at how Elixir does doc tests: in the form of an interactive REPL session: defmodule MyModule do
@doc """
Given a number, returns true if the number is even and else otherwise.
## Example
iex> MyModule.even?(2)
true
iex> MyModule.even?(3)
false
"""
def even?(number) do
rem(number, 2) == 0
end
end This would be a big change, and implies a REPL that everyone is familiar with. But it's a very low-friction way to write tests. Maybe as a custom testing framework when that hits stable... |
Just hit this roadblock today with a smaller bin project that I was working on and it was very annoying. Doc tests should be standard across all project types, not just library. |
Could rustdoc tests be turned into regular tests? That way a |
They cannot. You can write doctests that are expected to not compile (with the |
Just add |
https://github.com/dtolnay/trybuild is a library for testing compile errors. I think it's unlikely it will become part of libtest itself. |
I'd like to point out this comment by @Kromey:
While it would be impossible to use the For those tests we could automatically Alternatively, this could be done automatically (without a If you ask me, having the ability to execute doctests in private modules iff they are supposed to compile is a big benefit for internal documentation of private modules in a library, even if it has the limitation that the example must compile. Especially when this can be done without a breaking change or in a way where adding the attribute can be automated later. It is really annoying that we have to add |
This bit me today. I am writing a backend service, which has a JSON api. I wanted to document an example of valid JSON, for someone reading the code who is unfamiliar with rust. Conceptually this should only need to be a It would be good if I didn't have to resort to making a trivial |
This caught me today. It seems like very unintuitive behavior. Since I am still very new to the language, the docs are mostly for my own benefit and sanity. I know there is an unstable feature to scrape examples, but I really wish I could just write my examples outside of markdown blocks while still having them be within the same file of what they are documenting. |
I will humbly admit I'm not well versed in rustc or cargo internals, so I may be way off. I would still like to make a few comments based on what I would want and which behaviors I would expect as a developer when running
|
@daniel-pfeiffer People aren't always going to have the variety of perspectives at the early stages of a given exploration. We should be empathetic to that people come from all walks of life and may need some help to understand other perspectives. Your response here was very much not how we should help each other understand things, and that's putting aside that you're replying to something from almost 5 years ago. In addition to the empathy, please watch for context. Lastly, a lot of your response was loaded with emotional context that didn't do anything for the conversation. All feelings are valid, but how you act on them matters. This wasn't the place to vent about your feelings with how contributions are done. |
Yeah this caught me today too. I'm writing something that's not intended to be used as-is (such as a library) but is meant to be forked and expanded on for the users special case. Working examples in the documentation would be very valuable in demonstrating this use, I think. More-so than making potential users dig into tests. I understand that this is a tricky issue but since people are still commenting on it some 4 years after it was first reported, maybe it can be moved up on in priority? Having functioning examples in the documentation is such a powerful and useful feature it's a shame that it's limited in such a way. |
As suggested in the first comments, it could already work by providing a small fix in cargo, which would be the simplest way to achieve this feature. You can try your luck there directly. |
I was bit by this today too. I'm in favor of doctests just running, binary or not. It can be very useful when teaching Rust to be able to put a doctest in a binary. Given the number of people just here who have hit this - and bothered to comment - and the number of years this has bitten folks, it'd probably more of an expected behavior if they just always run, even in binaries. |
If it is tricky to run those doctests, maybe it can be easier to at least detect them and warn. /// ```
/// 2+2
/// ```
fn main() {
println!("Hello, world!");
}
|
Agreed. That would be helpful until an always-run-tests feature can (if possible and desired) be properly designed and implemented. I doubt implementing such a warning could break anyone's existing code. |
I am writing my backend in rust, and I would love to write doctests for the sql-related stuff, but my project is a binary 🤔 I really cannot see a reason to not want this, it does not change any existing programs. |
The best part is, that in rust by example there is no mention of doctest not working with bin targets: It should just work. |
This also bit me a while ago and every couple of months I'm searching the issues for progress because I do think this could greatly help people understand other peoples code and intentions. These are my main pain points:
If we don't want to break older binaries we could do smth like this: # Cargo.toml
[[bin]]
name = "mybin"
doctest = true # Defaulting to false |
2024 reporting in! Was very confused why my tests were not running after following rust by example. The part where it says On a cheerful note, it's a good time to drop this article. (Once more a lenghty bump on this topic) |
I spent an hour trying to figure out why my doctests wouldn't run. So happy I finally found this issue. |
Little update for everyone interested: discussions on how to fix this issue (re)started recently. As you might have guessed, it's a tricky issue to solve as it requires to completely change how doctests work. The "fun" part is allowing them to work on internal APIs (the current issue) and also allow them to work as if your crate is a dependency to showcase how to use an API (the original intent and current way they work). |
[Doc tests can't use crate internal APIs unfortunately.][internal-doc] I really want them to be able to, but for now, mark such tests as `ignore`. I was motivated to do this because it otherwise breaks `cargo t --all` for me. [internal-doc]: rust-lang/rust#50784
[Doc tests can't use crate internal APIs unfortunately.][internal-doc] I really want them to be able to, but for now, mark such tests as `ignore`. I was motivated to do this because it otherwise breaks `cargo t --all` for me. [internal-doc]: rust-lang/rust#50784
Just to keep everyone up-to-date: since #126245 was merged, the first step is now done. Here is the plan I currently have in mind for allowing this feature to work: just like currently, How does it work? If any doctest matches the parameter listed above, Now what happens if you want a doctest on a public/reexported item to still have access to local code? For that I suggest adding a new code block attribute named So from this point, the issue I can think of are:
I think that's it. Work on it began. A bit more patience folks. Binacry crate/private items doctests are coming! |
It's surprising that doctests sometimes don't run at all, and there's no warning about this.
cargo test --all
I'd expect the above to fail, but it looks like the doc-comment code is never ran. Even if I explicitly enable doctests for the binary:
they still aren't run.
(moved from rust-lang/cargo#5477)
The text was updated successfully, but these errors were encountered: