Lucretiel / lazy_format

Lazy formatting utility macro for rust
Mozilla Public License 2.0
124 stars 6 forks source link

Fail to format in a Chain #4

Closed oovm closed 3 years ago

oovm commented 3 years ago
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use joinery::JoinableIterator;
use lazy_format::lazy_format;

#[derive(Debug, Clone)]
pub struct Command {
    cmd: &'static str,
    args: Vec<String>,
    kvs: HashMap<&'static str, String>,
}

impl Display for Command {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let a = self.args.iter().map(|v| lazy_format!("{}", v));
        let kv = self.kvs.iter().map(|(k, v)| lazy_format!("{} = {}", k, v));
        write!(f, "\\{}({})", self.cmd, a.chain(kv).join_with(", "))
    }
}
error[E0271]: type mismatch resolving `<[closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76] as std::ops::FnOnce<((&&str, &std::string::String),)>>::Output == <ast::value::cmd::Command as std::fmt::Display>::fmt::{{closure}}#0::LazyFormat<[closure@C:\Users\Administrator\.cargo\registry\src\github.com-1ecc6299db9ec823\lazy_format-1.8.3\src\lib.rs:106:30: 108:10 v:_]>`
  --> projects\notedown-ast\src\ast\value\cmd.rs:17:43
   |
15 |         let a = self.args.iter().map(|v| lazy_format!("{}", v));
   |                                          --------------------- the found closure
16 |         let kv = self.kvs.iter().map(|(k, v)| lazy_format!("{} = {}", k, v));
   |                                               ----------------------------- the expected closure
17 |         write!(f, "\\{}({})", self.cmd, a.chain(kv).join_with(", "))
   |                                           ^^^^^ expected struct `<ast::value::cmd::Command as std::fmt::Display>::fmt::{{closure}}#1::LazyFormat`, found struct `<ast::value::cmd::Command as std::fmt::Display>::fmt::{{closure}}#0::LazyFormat`
   |
   = note: expected struct `<ast::value::cmd::Command as std::fmt::Display>::fmt::{{closure}}#1::LazyFormat<[closure@C:\Users\Administrator\.cargo\registry\src\github.com-1ecc6299db9ec823\lazy_format-1.8.3\src\lib.rs:106:30: 108:10 k:_, v:_]>`
              found struct `<ast::value::cmd::Command as std::fmt::Display>::fmt::{{closure}}#0::LazyFormat<[closure@C:\Users\Administrator\.cargo\registry\src\github.com-1ecc6299db9ec823\lazy_format-1.8.3\src\lib.rs:106:30: 108:10 v:_]>`
   = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>`

error[E0599]: no method named `join_with` found for struct `std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>` in the current scope
  --> projects\notedown-ast\src\ast\value\cmd.rs:17:53
   |
17 |         write!(f, "\\{}({})", self.cmd, a.chain(kv).join_with(", "))
   |                                                     ^^^^^^^^^ method not found in `std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>`
   | 
  ::: C:\Users\Administrator\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src\libcore\iter\adapters\chain.rs:15:1
   |
15 | pub struct Chain<A, B> {
   | ----------------------
   | |
   | doesn't satisfy `_: joinery::JoinableIterator`
   | doesn't satisfy `_: std::iter::Iterator`
   |
   = note: the method `join_with` exists but the following trait bounds were not satisfied:
           `std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>: std::iter::Iterator`
           which is required by `std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>: joinery::JoinableIterator`
           `&std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>: std::iter::Iterator`
           which is required by `&std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>: joinery::JoinableIterator`
           `&mut std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>: std::iter::Iterator`
           which is required by `&mut std::iter::Chain<std::iter::Map<std::slice::Iter<'_, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:15:38: 15:63]>, std::iter::Map<std::collections::hash_map::Iter<'_, &str, std::string::String>, [closure@projects\notedown-ast\src\ast\value\cmd.rs:16:38: 16:76]>>: joinery::JoinableIterator`

warning: unused import: `joinery::JoinableIterator`
 --> projects\notedown-ast\src\ast\value\cmd.rs:3:5
  |
3 | use joinery::JoinableIterator;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error: aborting due to 2 previous errors; 1 warning emitted

Some errors have detailed explanations: E0271, E0599.
For more information about an error, try `rustc --explain E0271`.
error: could not compile `notedown_ast`.

To learn more, run the command again with --verbose.

Process finished with exit code 101

Use simple format it works

use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use joinery::JoinableIterator;

#[derive(Debug, Clone)]
pub struct Command {
    cmd: &'static str,
    args: Vec<String>,
    kvs: HashMap<&'static str, String>,
}

impl Display for Command {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let a = self.args.iter().map(|v| format!("{}", v));
        let kv = self.kvs.iter().map(|(k, v)| format!("{} = {}", k, v));
        write!(f, "\\{}({})", self.cmd, a.chain(kv).join_with(", "))
    }
}
Lucretiel commented 3 years ago

Thank you for the report, will investigate

Lucretiel commented 3 years ago

This appears to be an issue with joinery, rather than lazy_format. Conveniently I am the author of both, so I'm going to transfer this issue to there.

Lucretiel commented 3 years ago

Whoops, I see your point about format working but lazy_format failing. Transferring back!

Lucretiel commented 3 years ago

I won't be able to test until later tonight, but upon re-read I think the issue is that, because each invocation of lazy_format! creates an opaque, anonymous, unique type, so a.chain(kv) can't succeed, because the two iterators (a and kv) have different element types. I'll try to figure out if there's a good way to account for this use case directly in lazy_format; I ran into a similar issue with lazy_format! being used in different branches of a match, for which I added a special syntax directly to lazy_format. In the meantime, the best solution is probably to use something like Either to unify the types (which is probably what a lazy_format solution would do anyway)

use either::Either;
// Don't need a lazy_format on `args` since we're just
// formatting each element directly
let a = self.args.iter().map(Either::Left);
let kv = self.kvs.iter().map(|(k, v)| lazy_format!("{} = {}", k, v)).map(Either::Right);
let joined = a.chain(kv).join_with(", ");
...
Lucretiel commented 3 years ago

While the issue is connected to lazy_format returning unique types, any library solution would necessarily have to be in joinery, since that's where support for joining heterogeneous sequences would need to be. I'm therefore closing this issue as outside of scope for lazy_format.