If we don't have any word to express the idea of !is_empty()
, maybe we should express it using sigil?
Below are my preliminary excuses for this "yet another sigil" proposal:
- Sigils anyway works well for many other common ideas (math, references, logic, etc.)
-
!is_empty()
is common enough to deserve its own sigil
- The result would be safe alternative to truthiness concept available in other programming languages
So, I propose prefix +
operator as !is_empty()
replacement.
It's not overloaded yet, and somehow it resembles non-empty meaning, therefore it could become a viable alternative for the current syntax. Further it could replace various other checks e.g. is_some()
, is_positive()
, is_ok()
, etc. however I'm not sure if we should go so far.
examples taken from the bottom of `rg '.is_empty()' -C 10` execution on `rust-lang/rust` repository
if !rustflags.is_empty() {
return Ok(rustflags);
}
if +rustflags {
return Ok(rustflags);
}
// First try RUSTFLAGS from the environment
if let Ok(a) = env::var(name) {
let args = a.split(' ')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string);
return Ok(args.collect());
}
// First try RUSTFLAGS from the environment
if let Ok(a) = env::var(name) {
let args = a.split(' ')
.map(str::trim)
.filter(|s| +s)
.map(str::to_string);
return Ok(args.collect());
}
if !rustdocflags.is_empty() {
self.compilation
.rustdocflags
.entry(unit.pkg.package_id().clone())
.or_insert(rustdocflags);
}
if +rustdocflags {
self.compilation
.rustdocflags
.entry(unit.pkg.package_id().clone())
.or_insert(rustdocflags);
}
let feats = self.bcx.resolve.features(unit.pkg.package_id());
if !feats.is_empty() {
self.compilation
.cfgs
.entry(unit.pkg.package_id().clone())
.or_insert_with(|| {
feats
.iter()
.map(|feat| format!("feature=\"{}\"", feat))
.collect()
});
}
let feats = self.bcx.resolve.features(unit.pkg.package_id());
if +feats {
self.compilation
.cfgs
.entry(unit.pkg.package_id().clone())
.or_insert_with(|| {
feats
.iter()
.map(|feat| format!("feature=\"{}\"", feat))
.collect()
});
}
if ret.is_empty() {
if !unsupported.is_empty() {
bail!(
"cannot produce {} for `{}` as the target `{}` \
does not support these crate types",
unsupported.join(", "),
unit.pkg,
bcx.target_triple()
)
}
if !+ret {
if +unsupported {
bail!(
"cannot produce {} for `{}` as the target `{}` \
does not support these crate types",
unsupported.join(", "),
unit.pkg,
bcx.target_triple()
)
}
if !to_add.is_empty() {
new_deps.push((*unit, to_add));
}
if +to_add {
new_deps.push((*unit, to_add));
}
assert!(result_vec.is_empty());
assert!(!+result_vec);
fn is_line_of_interest(line: &str) -> bool {
!line.split_whitespace()
.filter(|sub_string|
sub_string.contains("file://") &&
!sub_string.contains("file:///projects/")
)
.collect::<Vec<_>>()
.is_empty()
}
fn is_line_of_interest(line: &str) -> bool {
+line
.split_whitespace()
.filter(|sub_string|
sub_string.contains("file://") &&
!sub_string.contains("file:///projects/")
)
.collect::<Vec<_>>();
}
if is_file_of_interest(path) {
let err_vec = lint_file(path);
for err in &err_vec {
match *err {
LintingError::LineOfInterest(line_num, ref line) =>
println_stderr!("{}:{}\t{}", path.display(), line_num, line),
LintingError::UnableToOpenFile =>
println_stderr!("Unable to open {}.", path.display()),
}
}
!err_vec.is_empty()
} else {
false
}
if is_file_of_interest(path) {
let err_vec = lint_file(path);
for err in &err_vec {
match *err {
LintingError::LineOfInterest(line_num, ref line) =>
println_stderr!("{}:{}\t{}", path.display(), line_num, line),
LintingError::UnableToOpenFile =>
println_stderr!("Unable to open {}.", path.display()),
}
}
+err_vec
} else {
false
}
if line.is_empty() {
is_in_inline_code = false;
}
if !+line {
is_in_inline_code = false;
}
match self.0 {
None => other.is_empty(),
Some(f) => {
let other = other.trim();
for v in f.split(' ') {
if v == other {
return true;
}
}
false
}
}
match self.0 {
None => !+other,
Some(f) => {
let other = other.trim();
for v in f.split(' ') {
if v == other {
return true;
}
}
false
}
}
impl<'a> PartialEq<&'a str> for CpuInfoField<'a> {
fn eq(&self, other: &&'a str) -> bool {
match self.0 {
None => other.is_empty(),
Some(f) => f == other.trim(),
}
}
}
impl<'a> PartialEq<&'a str> for CpuInfoField<'a> {
fn eq(&self, other: &&'a str) -> bool {
match self.0 {
None => !+other,
Some(f) => f == other.trim(),
}
}
}
&mut |line| {
if !line.is_empty() {
Err(internal(&format!(
"compiler stdout is not empty: `{}`",
line
)))
} else {
Ok(())
}
},
&mut |line| {
if +line {
Err(internal(&format!(
"compiler stdout is not empty: `{}`",
line
)))
} else {
Ok(())
}
},
// Parse the dep-info into a list of paths
pub fn parse_dep_info(
pkg: &Package,
dep_info: &Path
) -> CargoResult<Option<Vec<PathBuf>>> {
let data = match paths::read_bytes(dep_info) {
Ok(data) => data,
Err(_) => return Ok(None),
};
let paths = data.split(|&x| x == 0)
.filter(|x| !x.is_empty())
.map(|p| util::bytes2path(p).map(|p| pkg.root().join(p)))
.collect::<Result<Vec<_>, _>>()?;
if paths.is_empty() {
Ok(None)
} else {
Ok(Some(paths))
}
}
// Parse the dep-info into a list of paths
pub fn parse_dep_info(
pkg: &Package,
dep_info: &Path
) -> CargoResult<Option<Vec<PathBuf>>> {
let data = match paths::read_bytes(dep_info) {
Ok(data) => data,
Err(_) => return Ok(None),
};
let paths = data.split(|&x| x == 0)
.filter(|x| +x)
.map(|p| util::bytes2path(p).map(|p| pkg.root().join(p)))
.collect::<Result<Vec<_>, _>>()?;
if +paths {
Ok(Some(paths))
} else {
Ok(None)
}
}
Some of non-trivial drawbacks of this syntax:
- It may be surprising that applying
+
to value results into bool
instead of a number
- In some cases code becomes obscure e.g. when type of value isn't immediately known
- Using it with negation results into noisy code
- On Fira Code and probably on other fonts it may look too close to deref syntax
- Assymmetry with prefix
-
However, it becomes better when:
- You read
+x
as "is x" and !+x
as "no x"
- You get accustomed to a new meaning of prefix
+
and not read it as "plus"